{"openapi":"3.1.0","info":{"title":"1delta API v1","version":"1.0.0","description":"Unified API for DeFi data and actions.\n\n## Authentication & rate limits\n\nMost endpoints are public. Send your API key as the `x-api-key` header to lift rate limits — without a key, requests are throttled to **10 per 15 minutes** per IP. Get a key at https://auth.1delta.io/.\n\n## Response Envelope\n\nEvery endpoint returns a consistent JSON envelope. Integrators can rely on the same top-level shape regardless of the endpoint called.\n\n### Success\n\n```json\n{\n  \"success\": true,\n  \"data\": { ... },\n  \"actions\": { \"transactions\": [...], \"permissions\": [...] } | null\n}\n```\n\n- **`data`** — Informational payload (market data, quotes, simulation results, user positions, etc.). Present on all responses; `null` when the endpoint only produces transaction calldata with no additional info.\n- **`actions`** — Transaction calldata and approval/permission transactions that the caller should execute on-chain. Only populated by action endpoints; `null` on data endpoints.\n  - `actions.transactions` — Array of `{ to, data, value }` objects ready to be sent as EVM transactions.\n  - `actions.permissions` — Envelope-level array of approval/permission transactions (ERC-20 approves, credit delegations) that must be executed **before** the main transactions. Each entry has a human-readable `description`.\n\n> **Note on `permissions` vs `permissionTxns`:** the envelope exposes a single deduplicated `actions.permissions` array. Inside richer action responses (e.g. loop alternatives), each individual quote also carries its own `permissionTxns` — the per-quote subset needed if that specific alternative is executed. Most integrators only need `actions.permissions`.\n\n### Error\n\n```json\n{\n  \"success\": false,\n  \"error\": { \"code\": \"MISSING_PARAM\", \"message\": \"marketUid is required\" }\n}\n```\n\n- **`error.code`** — Machine-readable error code (e.g. `MISSING_PARAM`, `INVALID_PARAM`, `ACTION_FAILED`, `NOT_FOUND`, `ORIGIN_FAILED`).\n- **`error.message`** — Human-readable description.\n\n---\n\n## Data (`/v1/data/*`)\nCacheable market data, prices, yields, and user positions. These endpoints always return `actions: null` — only the `data` field is populated.\n\n## Actions (`/v1/actions/*`)\nTransaction builders organized by category:\n- **Lending** — deposit, withdraw, borrow, repay, mode, enable-collateral, repay-with-atoken\n- **Loop** — leverage, close, collateral-swap, debt-swap, migrate (cross-lender), refinance (see also *Data › Loop* for range calculations)\n- **Swap** — spot (same-chain meta-aggregator), x-chain (bridge aggregation)\n- **Allocate** — batch operations\n\nAll action endpoints support **GET** (query parameters only) and **POST** (query parameters + JSON body for post-trade simulation). The `allocate` endpoint is POST-only.\n\nFor loop and swap operations, omit `account` for a **quote-only** response (`data` contains quotes, `actions` is `null`), or include it to build full transaction calldata (`actions` is populated with ready-to-send transactions).\n\n## Simulation (POST)\nPOST the same query parameters as GET, plus a JSON body with the user's current `balanceData` and `aprData`. The response includes the standard fields plus a `simulation` object in `data` with projected post-trade health factor, borrow capacity, and APR data.\n\n## Execution Modes (lending only)\n- **direct** (default): Raw protocol interaction (deposit/withdraw/borrow/repay only)\n- **proxy**: Routes through 1delta composer contracts\n"},"servers":[{"url":"https://portal.1delta.io","description":"Production"},{"url":"https://portal-staging.1delta.io","description":"Staging"},{"url":"http://localhost:8787","description":"Local Development"}],"security":[{"ApiKeyAuth":[]}],"tags":[{"name":"Data › Lending","description":"Lending market and pool data"},{"name":"Data › Loop","description":"Range calculations for loop operations (leverage, close, collateral-swap, debt-swap)"},{"name":"Data › Yields","description":"Yield data by asset, lender, or intrinsic value"},{"name":"Data › User Positions","description":"User lending/borrowing positions"},{"name":"Data › Prices","description":"Asset price data and history"},{"name":"Data › Token","description":"Token lists, balances, and available lending assets"},{"name":"Data › Vaults","description":"Vault data and pending withdrawals across Fluid, Gearbox, Morpho, Silo, Euler Earn, LSTs, savings, Lagoon, HyperCore, and GMX"},{"name":"Data › General","description":"Health, metadata, and other data endpoints"},{"name":"Actions › Index","description":"Endpoint directory for the Actions section"},{"name":"Actions › Lending","description":"Deposit, withdraw, borrow, repay, mode, enable-collateral"},{"name":"Actions › Loop","description":"Leverage, close, collateral-swap, debt-swap"},{"name":"Actions › Swap","description":"Spot swap and cross-chain swap"},{"name":"Actions › Vaults","description":"Vault deposit/withdraw (ERC-4626 + ERC-7540/7575/HyperCore) and the LST, savings, and GMX action builders"},{"name":"Actions › Midnight","description":"Morpho Midnight order-book: post/cancel your own limit offers (MAKE). Taking existing offers (lend/borrow) uses the standard Lending actions."},{"name":"Actions › Term","description":"Term Finance auctions (sealed-bid lock → reveal) and secondary-listing management. Servicing (repay/redeem/collateral) and secondary-market lending use the standard Lending actions."},{"name":"Actions › Allocate","description":"Batch multi-operation transactions"}],"x-tagGroups":[{"name":"Data","tags":["Data › Lending","Data › Loop","Data › Yields","Data › User Positions","Data › Prices","Data › Token","Data › Vaults","Data › General"]},{"name":"Actions","tags":["Actions › Index","Actions › Lending","Actions › Loop","Actions › Swap","Actions › Vaults","Actions › Midnight","Actions › Term","Actions › Allocate"]}],"paths":{"/v1/data/lending/latest":{"get":{"tags":["Data › Lending"],"summary":"Get latest lending data (paginated by lender)","operationId":"lending-latest","description":"Returns latest per-market lending data for the requested chains, scoped to a specific page of lender keys.\n\n**Pagination**\n\nBoth `chains` and `lenders` are required. A maximum of **20 lender keys** may be supplied per request. Use [`GET /v1/data/lending/lenders`](#operation/lending-lenders) to enumerate available lender keys (sorted by TVL) and page through them in batches of up to 20:\n\n```\nGET /v1/data/lending/lenders?chains=1,8453            → enumerate keys\nGET /v1/data/lending/latest?chains=1,8453&lenders=k1,…,k20    → page 1\nGET /v1/data/lending/latest?chains=1,8453&lenders=k21,…,k40   → page 2\n```\n\n**Breaking changes vs previous /lending/latest**\n\n- `lenders` is now required.\n- Hard cap of 20 lender keys per request.\n- Top-level `lenderKey` field is removed from items — use `lenderInfo.key`.\n\n**Lista DAO fixed-term (brokered) markets:** a market is brokered when its `terms[]` rate card is non-empty and `flags.variableBorrowDisabled === true`. Such markets report `variableBorrowRate = 0` but cannot be borrowed variably through 1delta — read `terms[]` for the available fixed terms and their APRs, and `broker` for the gateway contract. See the `MarketTerm` schema.\n\n**Morpho Midnight order-book markets (`MORPHO_MIDNIGHT_<id>`):** liquidity is an order book of maker offers, not a pool. Pass `includeOffers=true` to attach the live two-sided ladder to each such market's loan leg: `offers` (bids — the demand a borrower TAKES) and `lendOffers` (asks — the supply a lender TAKES), both best-first with per-level `aprPct`, `assets`, `assetsUsd`, and `cumulativeAssets`. Plain pool markets return no offers (their single rate suffices). This is the read side of **TAKE**; to fill offers use the standard [deposit](#operation/lending-deposit)/[borrow](#operation/lending-borrow) actions, and to **MAKE** your own offer see [`/v1/actions/midnight/make`](#operation/midnight-make).\n\n**Teller markets (`TELLER_<pool>`):** fixed-term, fixed-APR pool loans with **TIME-based liquidation** — collateral is seized only on a missed payment past the market window, never on a price move (no margin calls). `fixedTerm.model = \"teller\"`, `provider.kind = \"pool\"`, rolling duration up to `teller.maxLoanDuration`. ⚠ **AGGRESSIVE default terms — surface these prominently:** on default the borrower can lose their **ENTIRE escrowed collateral** (a liquidator seizes all of it, not just the amount owed — at a 50% LTV that's ~2× the borrowed value), and the grace window after the term can be **very short** (`params.market.teller.paymentDefaultDuration`, observed as low as **300 s / 5 min**). There is also an **upfront origination fee** (`teller.originationFeePercent` = `marketFeeBps` + `protocolFeeBps`, also on `fixedTerm.fees.originationFeePercent`). The market descriptor (`params.market.teller`) carries the read-side facts the UI should surface: **`implications`** (a ready-to-display string list — lead with the full-collateral-liquidation warning), **`paymentDefaultDuration`**, **`originationFeePercent`**, **`requiresBorrowerAttestation`** (this market only lets whitelisted borrowers open loans — a non-attested account is rejected at borrow time, so gate the borrow CTA on this flag), and **`marketOpen`** (a closed market reports `borrowingEnabled = false`). Borrowing is one atomic `POST /v1/actions/lending/deposit-and-borrow`; closing is a FULL `POST /v1/actions/lending/withdraw-and-repay` (repays everything and releases all collateral — Teller has no partial collateral withdrawal; `posId` = the `bidId` to close). Borrower positions are `bidId`-keyed sub-accounts.\n\n**Term Finance markets (`TERM_FINANCE_<termRepoId>`):** fixed-rate, fixed-maturity tri-party repo. One repo per maturity, so a pair has many lender keys that differ only by date — read `lenderInfo.name` (e.g. \"Term USDC / wstETH — 2026-09-03\") rather than the raw key. `fixedTerm.model = \"term\"`, `provider.kind = \"auction\"`, `flags.variableBorrowDisabled = true` (there is no variable rate — a `variableBorrowRate` of 0 is NOT a free borrow).\n\n⚠ **Borrowing is only possible inside a scheduled sealed-bid auction round, and most repos are between rounds at any given time** (live Ethereum book: typically ~3 of ~84). `fixedTerm.auction` carries the window and is the gate:\n\n- **`canBorrow`** — gate the borrow CTA on this, NOT on `status` or on the presence of a rate. It is true only while a round is accepting submissions.\n- **`canLend`** — deliberately independent of `canBorrow`: lending also works between rounds by buying repo tokens on the secondary market, so a closed round leaves the market lend-only, not inert. Do not grey out the whole market.\n- **`status`** — `upcoming` | `open` | `revealing` (bidding shut, prices revealing) | `closed` (no round listed).\n- **`secondsUntilClose`**, **`startTime`**, **`revealTime`** (the deadline to act), **`endTime`** — unix seconds; derive a live countdown from `revealTime` rather than trusting `secondsUntilClose` against a cached response.\n- **`minBorrowAmount`** / **`minLendAmount`** — a real per-round floor in loan-token base units (e.g. 1000 USDC). A smaller amount cannot be submitted at all, so validate before building.\n- **`implications`** — ready-to-display string list, most important first (same convention as `params.market.teller.implications`).\n\n`terms[]` is emitted ONLY while `canBorrow` is true, so an empty rate card on a Term market means \"not borrowable right now\", not \"no offers\". When a round is open on a repo that has never cleared, `terms[]` is legitimately empty and `variableBorrowRate` is 0: the rate is whatever you bid, set at clearing. **Any rate shown outside an open round is the previous round's clearing rate — historical, not obtainable** (it still prices the secondary lend book, which is why it is reported at all).\n\n**Term sheets (`termSheet`):** every market carries a structured description of its lend and borrow offer under one shape, for every lender we serve — pool lenders, fixed-term lenders, CDPs and vaults alike. `termSheet.supply` and `termSheet.borrow` each answer rate, maturity, fees, exit terms, liquidation, counterparty and availability; `termSheet.governance`, `.oracle`, `.utilization` and `.constraints` describe the market as a whole. Absence of a side is meaningful: no `borrow` means the market cannot be borrowed.\n\nRead `info.headline` and `info.tags` for a ready-to-render summary, and `info.implications[]` (ordered most-important-first) for the consequences that a rate alone hides — a Teller borrow can lose its ENTIRE collateral after a grace window as short as 300 s, a TermMax lender can be settled in collateral instead of the asset they lent, and a Liquity trove can be redeemed at par while perfectly healthy. `coverage` distinguishes \"does not apply here\" (`notApplicable`, e.g. Teller genuinely has no oracle) from \"not classified yet\" (`pending`) — a missing block is never a claim of absence.\n\nEvery string field is an OPEN enum: new members are added additively and MUST NOT break a client. Give every `switch` a `default` branch and fall back to `info.headline`, which is always populated.\n\n**Oracle risk:** each market carries an `oracleInfo` object classifying its price oracle's feed correctness (provider, reported vs intended pair, a 0–100 `worstScore`/`worstBand`, and `flags` such as `wrong-asset`/`correlated-proxy`/`cross-numeraire`). This is distinct from the price-staleness signal in `risk.breakdown[oracle]`. See the `OracleInfo` schema for the full scoring model.","parameters":[{"name":"chains","in":"query","required":true,"description":"Chain IDs to query (repeatable, CSV also accepted)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["1","8453"]},{"name":"lenders","in":"query","required":true,"description":"Lender keys to fetch (repeatable, CSV also accepted). Max 20 per request.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["AAVE_V3","COMPOUND_V3_USDC"]},{"name":"maxRiskScore","in":"query","schema":{"type":"integer","default":4},"description":"Max risk score (1–5). Defaults to 4.","example":4},{"name":"terms","in":"query","schema":{"type":"string","enum":["digest","full","none"],"default":"digest"},"description":"Term-sheet depth attached to every market as `termSheet`. `digest` (default) is the compact form — headline, tags, rate/maturity/exit/liquidation summary and the exposure ROLLUP, but no `items[]` and no long prose. `full` inlines the complete sheet including `backedBy.items[]` / `acceptedCollateral.items[]` and `info.description` + `info.implications[]`. `none` omits the field entirely. Every exposure item carries its own `marketUid`, so `digest` is not a dead end — resolve the ones you need in one call.","example":"full"},{"name":"includeOffers","in":"query","schema":{"type":"boolean","default":false},"description":"Order-book markets (Morpho Midnight) only. When `true`, attach the live maker-offer ladder to each order-book market’s loan leg (`offers` = bids, `lendOffers` = asks). Ignored for pool markets. Defaults to `false`.","example":true}],"responses":{"200":{"description":"Latest lending data","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LendingLatestResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/lenders":{"get":{"tags":["Data › Lending"],"summary":"Enumerate available lenders","operationId":"lending-lenders","description":"Lightweight enumeration of every `(chainId, lenderKey)` pair that has data for the requested chains, sorted by `tvlUsd` descending.\n\nUse this endpoint to discover the lender keys to page through [`GET /v1/data/lending/latest`](#operation/lending-latest), which is hard-capped at 20 lender keys per request.\n\n```\nGET /v1/data/lending/lenders?chains=1,8453            → enumerate keys\nGET /v1/data/lending/latest?chains=1,8453&lenders=k1,…,k20    → page 1\nGET /v1/data/lending/latest?chains=1,8453&lenders=k21,…,k40   → page 2\n```","parameters":[{"name":"chains","in":"query","required":true,"description":"Chain IDs to query (repeatable, CSV also accepted)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["1","10","8453"]},{"name":"lenders","in":"query","description":"Filter by lender keys (repeatable, CSV also accepted). Defaults to all lenders.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["AAVE_V3"]},{"name":"maxRiskScore","in":"query","schema":{"type":"integer","default":4},"description":"Max risk score (1–5). Defaults to 4.","example":4},{"name":"minTvl","in":"query","schema":{"type":"number"},"description":"Filter out lender entries with `tvlUsd` below this USD threshold. Applied as a HAVING clause after aggregation. Returns 400 if non-numeric.","example":1000000},{"name":"count","in":"query","schema":{"type":"integer"},"description":"Optional cap on the number of items returned.","example":20}],"responses":{"200":{"description":"Available lenders sorted by TVL","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LendingLendersResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/pools":{"get":{"tags":["Data › Lending"],"summary":"Get lending pools","description":"Returns paginated lending pool data with optional filters and sorting.\n\n**Server-side defaults** (applied when the parameter is omitted; pass an explicit value to override):\n| Parameter | Default |\n|-----------|---------|\n| `minUtil` | `0.1` |\n| `maxUtil` | `0.9` |\n| `minTvlUsd` | `100000` (Ethereum, chainId 1) / `25000` (all other chains) |\n| `maxRiskScore` | `4` (medium) |\n\nTo disable a default filter, pass `0` (e.g. `minUtil=0`).\n\n**Oracle risk:** each pool carries an `oracleInfo` object classifying its price oracle's feed correctness (provider, reported vs intended pair, a 0–100 `worstScore`/`worstBand`, and `flags` such as `wrong-asset`/`correlated-proxy`/`cross-numeraire`). This is distinct from the price-staleness signal in `risk.breakdown[oracle]`. See the `OracleInfo` schema for the full scoring model.","parameters":[{"name":"chainId","in":"query","schema":{"type":"string"},"description":"Filter by chain ID","example":"1"},{"name":"lender","in":"query","schema":{"type":"string"},"description":"Filter by lender key (e.g. AAVE_V3)","example":"AAVE_V3"},{"name":"underlyings","in":"query","schema":{"type":"string"},"description":"Comma-separated token addresses (0x-prefixed)"},{"name":"assetGroups","in":"query","schema":{"type":"string"},"description":"Comma-separated asset group names","example":"ETH"},{"name":"minYield","in":"query","schema":{"type":"number"},"description":"Minimum deposit rate"},{"name":"maxYield","in":"query","schema":{"type":"number"},"description":"Maximum deposit rate"},{"name":"minUtil","in":"query","schema":{"type":"number","default":0.1},"description":"Minimum utilization (0-1). Defaults to 0.1 when omitted."},{"name":"maxUtil","in":"query","schema":{"type":"number","default":0.9},"description":"Maximum utilization (0-1). Defaults to 0.9 when omitted."},{"name":"minTvlUsd","in":"query","schema":{"type":"number"},"description":"Minimum total liquidity in USD. Defaults to 100000 on Ethereum (chainId 1), 25000 on other chains."},{"name":"maxTvlUsd","in":"query","schema":{"type":"number"},"description":"Maximum total liquidity in USD"},{"name":"minDeposits","in":"query","schema":{"type":"number"},"description":"Minimum total deposits (native units)"},{"name":"maxDeposits","in":"query","schema":{"type":"number"},"description":"Maximum total deposits (native units)"},{"name":"minDebt","in":"query","schema":{"type":"number"},"description":"Minimum total debt (native units)"},{"name":"maxDebt","in":"query","schema":{"type":"number"},"description":"Maximum total debt (native units)"},{"name":"minLiquidity","in":"query","schema":{"type":"number"},"description":"Minimum total liquidity (native units)"},{"name":"maxLiquidity","in":"query","schema":{"type":"number"},"description":"Maximum total liquidity (native units)"},{"name":"minDebtUsd","in":"query","schema":{"type":"number"},"description":"Minimum total debt in USD"},{"name":"maxDebtUsd","in":"query","schema":{"type":"number"},"description":"Maximum total debt in USD"},{"name":"minLiquidityUsd","in":"query","schema":{"type":"number"},"description":"Minimum total liquidity in USD"},{"name":"maxLiquidityUsd","in":"query","schema":{"type":"number"},"description":"Maximum total liquidity in USD"},{"name":"maxRiskScore","in":"query","schema":{"type":"integer","default":4},"description":"Maximum risk score (1–5). Defaults to 4 (medium) when omitted."},{"name":"includeExposures","in":"query","schema":{"type":"boolean","default":false},"description":"Include config exposure data per pool. Opt-in; omit or false to skip (expensive)."},{"name":"sortBy","in":"query","schema":{"type":"string","enum":["depositRate","variableBorrowRate","stableBorrowRate","intrinsicYield","utilization","totalDeposits","totalDebt","totalLiquidity","totalDepositsUsd","totalDebtUsd","totalLiquidityUsd"],"default":"totalDepositsUsd"},"description":"Sort field"},{"name":"sortDir","in":"query","schema":{"type":"string","enum":["ASC","DESC"],"default":"DESC"},"description":"Sort direction"},{"name":"start","in":"query","schema":{"type":"integer","default":0},"description":"Pagination offset"},{"name":"count","in":"query","schema":{"type":"integer","default":100},"description":"Page size (default 100, max 1000)","example":10}],"responses":{"200":{"description":"Pool data","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/PoolsResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/pools/by-config":{"get":{"tags":["Data › Lending"],"summary":"Get pools grouped by config","description":"For each (lender, chain, configId) combination, returns which markets are eligible as collateral or borrowable.\n\nEach item includes a `collaterals` and `borrowables` array with per-market rate, factor, and TVL data. Either array is `null` when no markets qualify. A market absent from a config entry has both collateral and debt disabled for that config.\n\nUseful for building position-builder UIs that need to know which assets can be paired within a given pool configuration (e.g. e-mode categories, isolated pools).","parameters":[{"name":"chains","in":"query","schema":{"type":"string"},"description":"Comma-separated chain IDs","example":"1,8453"},{"name":"lenders","in":"query","schema":{"type":"string"},"description":"Comma-separated lender keys","example":"AAVE_V3"}],"responses":{"200":{"description":"Config pool breakdown per lender/chain/configId","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","additionalProperties":true},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/pairs":{"get":{"tags":["Data › Lending"],"summary":"Get lending pairs","description":"Returns paginated leverage pair data with optional filters and sorting. Backed by the same origin route as `/v1/data/lending/pairs/leverage`, so it shares that endpoint's behaviour — including dropping pairs whose collateral leg has no remaining supply capacity (`includeIlliquid=true` keeps them).\n\n**Lista DAO fixed-term (brokered) debt side:** when the short (debt) market is brokered, `variableBorrowDisabledShort` is `true` and `termsShort[]` lists the fixed-term loop options (each a `MarketTerm`). The pair has one loop option per term rather than a single variable-rate loop — use the per-term APR from `termsShort[]` in place of `variableBorrowRateShort` (which is `0`/undefined for these pairs).","parameters":[{"name":"chainId","in":"query","schema":{"type":"string"},"description":"Filter by chain ID","example":"1"},{"name":"lender","in":"query","schema":{"type":"string"},"description":"Filter by lender key","example":"AAVE_V3"},{"name":"assetGroupLong","in":"query","schema":{"type":"string"},"description":"Filter by collateral asset group","example":"ETH"},{"name":"assetGroupShort","in":"query","schema":{"type":"string"},"description":"Filter by debt asset group","example":"USDC"},{"name":"minApr","in":"query","schema":{"type":"number"},"description":"Minimum total APR"},{"name":"minLeverage","in":"query","schema":{"type":"number"},"description":"Minimum max leverage"},{"name":"minLiquidityUsd","in":"query","schema":{"type":"number"},"description":"Minimum liquidity in USD"},{"name":"includeIlliquid","in":"query","schema":{"type":"boolean","default":false},"description":"Keep pairs whose collateral leg has no remaining supply capacity (un-openable at any size). Off by default."},{"name":"sortBy","in":"query","schema":{"type":"string","enum":["aprTotal","maxLeverage","totalDepositsUsdLong","totalDebtUsdShort"]},"description":"Sort field","example":"aprTotal"},{"name":"sortDir","in":"query","schema":{"type":"string","enum":["asc","desc"],"default":"desc"},"description":"Sort direction"},{"name":"start","in":"query","schema":{"type":"integer","default":0},"description":"Pagination start index"},{"name":"count","in":"query","schema":{"type":"integer","default":50},"description":"Page size","example":10}],"responses":{"200":{"description":"Pair data","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LeveragePairsResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/pairs/leverage":{"get":{"tags":["Data › Lending"],"summary":"Browse all leverage pairs","description":"Returns raw materialized view rows with all rate and liquidity fields. Supports single-chain address filtering or cross-chain asset-group mode.\n\nPairs whose COLLATERAL leg has no remaining supply capacity (`depositableLong <= 0`) are dropped by default — a market that cannot take the deposit is not an opportunity at any rate, and those rows sort to the top of an APR ranking precisely because nobody can reach them. `depositableLong: null` means uncapped, i.e. unlimited, and always passes. `includeIlliquid=true` keeps them.","parameters":[{"name":"chainId","in":"query","schema":{"type":"string"},"description":"Single chain ID (address-based filtering). Omit for cross-chain asset-group mode."},{"name":"chainIds","in":"query","schema":{"type":"string"},"description":"Comma-separated chain IDs for multi-chain (asset-group filtering)"},{"name":"lender","in":"query","schema":{"type":"string"},"description":"Lender filter (UPPER_SNAKE_CASE)","example":"AAVE_V3"},{"name":"minApr","in":"query","schema":{"type":"number"},"description":"Minimum total APR"},{"name":"maxApr","in":"query","schema":{"type":"number"},"description":"Maximum total APR"},{"name":"minLeverage","in":"query","schema":{"type":"number"},"description":"Minimum leverage"},{"name":"assetLong","in":"query","schema":{"type":"string"},"description":"Long asset address (single-chain mode)"},{"name":"assetShort","in":"query","schema":{"type":"string"},"description":"Short asset address (single-chain mode)"},{"name":"assetGroupLong","in":"query","schema":{"type":"string"},"description":"Long asset group (ILIKE partial match)","example":"ETH"},{"name":"assetGroupShort","in":"query","schema":{"type":"string"},"description":"Short asset group (ILIKE partial match)","example":"USDC"},{"name":"minDepositApr","in":"query","schema":{"type":"number"},"description":"Min effective deposit APR (deposit_rate + intrinsic_yield)"},{"name":"minLtv","in":"query","schema":{"type":"number"},"description":"Minimum LTV ratio (0-1)"},{"name":"maxBorrowRate","in":"query","schema":{"type":"number"},"description":"Max effective borrow rate (borrow_rate + intrinsic_yield)"},{"name":"includeIlliquid","in":"query","schema":{"type":"boolean","default":false},"description":"Keep pairs whose collateral leg has no remaining supply capacity (un-openable at any size). Off by default."},{"name":"minLiquidityUsdLong","in":"query","schema":{"type":"number"},"description":"Min collateral-side liquidity USD. This is withdrawable CASH in the market, NOT deposit capacity — see `includeIlliquid` for the capacity gate."},{"name":"minDepositsUsdLong","in":"query","schema":{"type":"number"},"description":"Min collateral-side deposits USD"},{"name":"maxUtilizationLong","in":"query","schema":{"type":"number"},"description":"Max collateral-side utilization (0-1)"},{"name":"minBorrowLiquidityUsd","in":"query","schema":{"type":"number"},"description":"Min debt-side borrow liquidity USD"},{"name":"minDebtUsdShort","in":"query","schema":{"type":"number"},"description":"Min debt-side total debt USD"},{"name":"maxUtilizationShort","in":"query","schema":{"type":"number"},"description":"Max debt-side utilization (0-1)"},{"name":"start","in":"query","schema":{"type":"integer","default":0},"description":"Pagination offset"},{"name":"count","in":"query","schema":{"type":"integer","default":50},"description":"Page size (max 100)"},{"name":"sortBy","in":"query","schema":{"type":"string","enum":["aprTotal","maxLeverage","depositRateLong","variableBorrowRateShort","intrinsicYieldLong","intrinsicYieldShort","totalDepositsUsdLong","totalDepositsUsdShort","totalDebtUsdLong","totalDebtUsdShort","totalLiquidityUsdLong","totalLiquidityUsdShort","borrowLiquidityShort"],"default":"aprTotal"},"description":"Sort field"},{"name":"sortDir","in":"query","schema":{"type":"string","enum":["ASC","DESC"],"default":"DESC"},"description":"Sort direction"}],"responses":{"200":{"description":"Paginated leverage pairs","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LeveragePairsResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/pairs/optimize":{"get":{"tags":["Data › Lending"],"summary":"Collateral ⇄ debt optimizer","description":"Filter pairs by either or both sides, then optionally compute the opposite-side amount per row.\n\n## Asset selection\n`collaterals` / `debts` are reinterpreted by chain mode:\n- **Single chain** (`chainId`): values are token addresses.\n- **Multi-chain or no chain**: values are asset groups.\n\nUse `collateralGroups` / `debtGroups` to force group semantics in any chain mode.\nGroup params and address params on the same side are OR'd.\n\n### The native asset, and why you usually want groups\nThe native asset is spelled as the **zero address** in every 1delta data payload. `0xEeee…EEeE` is an encoding-layer sentinel — it is accepted on *action* inputs and normalised away, but it is never served here and **never matches an address filter**, so a client filtering by it silently gets zero rows.\n\nNative and wrapped-native markets are different rows (`0x0` on Fluid’s ETH vaults, `0xc02a…` on Aave WETH) but share one `assetGroup` (`ETH`). Selecting by group therefore returns **both** — which is what you want whenever the two are interchangeable for your purpose (a migrate bridges them by wrapping, a leverage loop can open on either). It also removes any need for a per-chain wrapped-native address table in the client. Each asset also carries `props.isNative` + `props.wrapped` (native rows) and `props.wnative` (wrapped rows) if you need to tell them apart after the fact.\n\n## Amount params\nThe collateral and debt sides are **independent** — supply an amount on either side, both, or neither:\n- `collateralAmount` (token units; requires exactly one collateral asset)\n- `collateralAmountUsd` (USD; multi-asset OK)\n- `debtAmount` (token units; requires exactly one debt asset)\n- `debtAmountUsd` (USD; multi-asset OK)\n\nA collateral input adds `maxDebtAmount` + `maxDebtAmountUsd` to each row; a debt input adds `minCollateralAmount` + `minCollateralAmountUsd`. Supplying both a collateral **and** a debt amount returns **both** column pairs. Within a single side the token-unit and `*Usd` forms are mutually exclusive (400 otherwise).\nUn-openable pairs are dropped by default on BOTH legs (`includeIlliquid=true` keeps them). Unconditionally: a collateral leg with no remaining supply capacity (`depositableLong <= 0` — a full or deliberately zeroed supply cap, where the market returns 0 for a max-deposit query). Additionally, when an amount is supplied: a debt leg whose borrow liquidity cannot fund the resulting debt, and a collateral leg whose capacity cannot absorb the required collateral. With no amount supplied, a small-but-real capacity is a real (smaller) opportunity and stays listed; `depositableLong: null` means uncapped and always passes.\n\n## Depth-aware APR\nWhenever an amount is supplied, each row also carries the EFFECTIVE APR at the computed notional: `borrowAprAtAmount`, `depositAprAtAmount`, `netAprAtAmount`. Each is the headline effective rate (`depositAprLong` / `borrowAprShort`, which fold in intrinsic yield + rewards) with only its ORGANIC (IRM) component re-priced at the utilization the position moves the pool to — intrinsic + rewards are size-invariant. So a curve lender that borrows near 0% at 0 notional prices materially higher at a real size, and `netAprAtAmount` is comparable to `aprTotal` but at the position's actual size/leverage. Pass `depth=true` to additionally receive the raw `borrowDepthShort`/`supplyDepthLong` grids.","parameters":[{"name":"chainId","in":"query","schema":{"type":"string"},"description":"Single chain ID. When set, asset filters operate on token addresses."},{"name":"chainIds","in":"query","schema":{"type":"string"},"description":"CSV of chain IDs. When two or more, asset filters operate on asset groups.","example":"1,10,8453"},{"name":"lender","in":"query","schema":{"type":"string"},"description":"Single lender key","example":"AAVE_V3"},{"name":"lenders","in":"query","schema":{"type":"string"},"description":"CSV of lender keys (prefix-expanded)","example":"AAVE_V3,AAVE_V4,SPARK"},{"name":"excludeLenders","in":"query","schema":{"type":"string"},"description":"CSV of lender keys to exclude (prefix-expanded)"},{"name":"collaterals","in":"query","schema":{"type":"string"},"description":"CSV of long-side filters. Token addresses (single chain) or asset groups (multi/no chain).","example":"WETH,wstETH"},{"name":"debts","in":"query","schema":{"type":"string"},"description":"CSV of short-side filters. Same dual semantics as `collaterals`."},{"name":"collateralGroups","in":"query","schema":{"type":"string"},"description":"CSV of long-side asset groups (works in any chain mode).","example":"USDC,USDT,DAI"},{"name":"debtGroups","in":"query","schema":{"type":"string"},"description":"CSV of short-side asset groups (works in any chain mode)."},{"name":"collateralTags","in":"query","schema":{"type":"string"},"description":"CSV of property flags the collateral (long) asset must carry, e.g. `eth`, `btc`, `native`, `wnative`, `stablecoin`, `savings`, `lst`, `lrt`, `pendle`, `rwa`. AND-ed with any `collaterals`/`collateralGroups` selection (narrows). Denomination flags (`eth`/`btc`) cover canonical base tokens only, not LST/derivative wrappers.","example":"lst"},{"name":"collateralTagsMode","in":"query","schema":{"type":"string","enum":["any","all"],"default":"any"},"description":"How to match multiple `collateralTags`: `any` (has at least one) or `all` (has every one)."},{"name":"debtTags","in":"query","schema":{"type":"string"},"description":"CSV of property flags the debt (short) asset must carry (e.g. `eth`, `btc`, `native`, `wnative`, `stablecoin`, `savings`, `lst`, `lrt`, `pendle`, `rwa`). Same semantics as `collateralTags`. Example: `collateralTags=lst&debtTags=wnative` for leveraged staking."},{"name":"debtTagsMode","in":"query","schema":{"type":"string","enum":["any","all"],"default":"any"},"description":"How to match multiple `debtTags`: `any` or `all`."},{"name":"includeExpired","in":"query","schema":{"type":"boolean","default":false},"description":"Include pairs whose collateral or debt is an expired Pendle PT. Excluded by default (judged live off `props.pendle.expiry`/`expired`)."},{"name":"collateralAmount","in":"query","schema":{"type":"number"},"description":"Token-unit collateral amount. Requires exactly one collateral asset."},{"name":"collateralAmountUsd","in":"query","schema":{"type":"number"},"description":"USD collateral amount. Multi-asset selections OK."},{"name":"debtAmount","in":"query","schema":{"type":"number"},"description":"Token-unit debt amount. Requires exactly one debt asset."},{"name":"debtAmountUsd","in":"query","schema":{"type":"number"},"description":"USD debt amount. Multi-asset selections OK."},{"name":"depth","in":"query","schema":{"type":"boolean","default":false},"description":"When true, also return the raw `borrowDepthShort`/`supplyDepthLong` rate-at-depth grids. The `*AtAmount` depth-aware scalars are returned whenever an amount is supplied, regardless of this flag."},{"name":"includeIlliquid","in":"query","schema":{"type":"boolean","default":false},"description":"By default the optimizer drops collateral legs with no remaining supply capacity, and — when an amount is supplied — pairs whose borrow liquidity cannot fund the resulting debt or whose collateral capacity cannot absorb the required collateral. Set true to keep such (un-openable) pairs."},{"name":"minApr","in":"query","schema":{"type":"number"},"description":"Minimum total APR"},{"name":"maxApr","in":"query","schema":{"type":"number"},"description":"Maximum total APR"},{"name":"minLeverage","in":"query","schema":{"type":"number"},"description":"Minimum leverage"},{"name":"minDepositApr","in":"query","schema":{"type":"number"},"description":"Min deposit APR including intrinsic yield (long side)"},{"name":"maxBorrowRate","in":"query","schema":{"type":"number"},"description":"Max borrow rate including intrinsic yield (short side)"},{"name":"minLtv","in":"query","schema":{"type":"number"},"description":"Minimum LTV (0-1)"},{"name":"maxUtilizationLong","in":"query","schema":{"type":"number"},"description":"Max collateral-side utilization (0-1)"},{"name":"maxUtilizationShort","in":"query","schema":{"type":"number"},"description":"Max debt-side utilization (0-1)"},{"name":"minLiquidityUsdLong","in":"query","schema":{"type":"number"},"description":"Min collateral-side liquidity USD. This is withdrawable CASH in the market, NOT deposit capacity — capacity is gated separately (see `includeIlliquid`)."},{"name":"minBorrowLiquidityUsd","in":"query","schema":{"type":"number"},"description":"Min debt-side borrow liquidity USD"},{"name":"minDepositsUsdLong","in":"query","schema":{"type":"number"},"description":"Min collateral-side deposits USD"},{"name":"minDebtUsdShort","in":"query","schema":{"type":"number"},"description":"Min debt-side total debt USD"},{"name":"maxRiskScore","in":"query","schema":{"type":"number","default":4},"description":"Backwards-compat alias for `maxConfigRiskScore`"},{"name":"maxConfigRiskScore","in":"query","schema":{"type":"number","default":4},"description":"Max config risk score"},{"name":"maxTokenRiskScore","in":"query","schema":{"type":"number"},"description":"Max token risk score"},{"name":"maxChainRiskScore","in":"query","schema":{"type":"number"},"description":"Max chain risk score"},{"name":"maxLenderRiskScore","in":"query","schema":{"type":"number"},"description":"Max lender risk score"},{"name":"start","in":"query","schema":{"type":"integer","default":0},"description":"Pagination offset"},{"name":"count","in":"query","schema":{"type":"integer","default":50},"description":"Page size (max 100)"},{"name":"sortBy","in":"query","schema":{"type":"string","enum":["aprTotal","aprBase","maxLeverage","ltv","depositAprLong","borrowAprShort","totalDepositsUsdLong","totalDepositsUsdShort","totalDebtUsdLong","totalDebtUsdShort","totalLiquidityUsdLong","totalLiquidityUsdShort","utilizationLong","utilizationShort","borrowLiquidityShort"],"default":"aprTotal"},"description":"Sort field"},{"name":"sortDir","in":"query","schema":{"type":"string","enum":["ASC","DESC"],"default":"DESC"},"description":"Sort direction"}],"responses":{"200":{"description":"Optimizer pairs","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/OptimizerPairsResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/comparables":{"get":{"tags":["Data › Lending"],"summary":"Comparable rates for one pair","description":"The **N best comparable rates for a single pair**, priced at the caller’s size and holding period. One round-trip, small payload — built to sit behind a rate pill next to a borrow input.\n\nUnlike `/pairs/optimize` (rank every leveraged pair), this answers the question a borrow form asks: *I am about to borrow X of asset D against collateral C — what are the best rates for that same trade, at my size, over my horizon?*\n\n## Three rates, deliberately kept apart\n- `aprPct` — what the venue advertises: spot at 0 notional, or top of book.\n- `aprAtAmountPct` — the same rate at **your size**. A utilization pool re-prices its whole balance to the post-borrow rate (marginal == average); an order book fills cheapest-first, so this is the VWAP of the tiers consumed. Null without an amount.\n- `effectiveAprPct` — the size-priced rate normalized to `horizonDays`. **This is what `rank` sorts on.**\n\n## Horizon normalization\nA 4-week fixed rate and a floating pool are not comparable until both are priced over the same holding period. Each venue supplies a repayment rule: a Lista loan exited early pays roughly half the remaining term’s interest (`r·(T+h)/(2h)` — 1%/1yr exited at 6 months is **1.5% effective**); Midnight/Term charge no penalty but exiting means a resale at the then-current price (`horizon.priceRisk`); Exactly rebates unassigned earnings; a CDP’s one-time origination fee amortizes over the horizon, so it dominates on short holds. A horizon that outlasts the term assumes a roll at today’s rate and reports `locked: false`.\nAlways render `horizon.locked` and `horizon.assumptions` next to the number — a normalized rate is not a contractual one.\n\n## Obtainability is part of the answer\nTerm Finance originates only inside periodic sealed-bid rounds; between rounds its rate card still quotes the LAST round’s clearing rate — real, but not takeable. Those rows are dropped by default and, with `includeUnobtainable=true`, returned flagged (`obtainable: false`, `quoteBasis: \"last-clearing\"`) and ranked last. A venue that cannot fund the requested size is marked `depth.capped` and demoted rather than ranked on a rate it can’t honour.\n\n## Comparability filters (what makes this a comparison rather than a list)\n**Depth band** — `floor = min(minLiquidityRatio × anchor, liquidityFloorCapUsd)`, default `min(50% × anchor, $10M)`. A venue an order of magnitude shallower is a different product, and it is exactly those markets that quote absurd rates — a near-empty isolated market with reward emissions prices borrowing at **−25%**, a real number and a useless comparison against a $5B pool. The cap is not optional: a pure ratio breaks on the biggest pairs, where half of a $5B pool is a $2.5B floor nothing clears, so the comparison would come back empty exactly where alternatives matter most. The anchor is `referenceMarketUid`'s own depth when given, else the deepest candidate. `droppedIlliquid` + `liquidityFloorUsd` report it — the filter is never silent; `minLiquidityUsd` replaces the formula and `includeIlliquid=true` turns it off.\n**Stale venues.** A lender whose ingest lagged past the freshness window drops out of the underlying pairs view — which reads exactly like \"that lender doesn't offer this pair\". Those rows are excluded from ranking but **counted** (`droppedStale`, `staleMaxHours`), because a $19B deployment vanishing with no signal is worse than a short list. `includeStale=true` ranks them anyway.\n**One collateral basis.** When the caller pins no collateral (a standalone borrow form knows none), comparing \"borrow USDC\" across every collateral in existence is not a comparison. The server picks ONE representative collateral per chain — first hit in `collateralPreference` (default `ETH,BTC,USDC,USDT`), else the collateral group backing the deepest markets, which resolves to the wrapped native where neither is listed — and compares only venues backed by it. The choice comes back as `collateralBasis` (e.g. `{\"1\":\"ETH\"}`) so the UI can say *\"vs WETH-backed\"*. An explicit `collateral`/`collateralGroups` always wins and disables this.\n\n## Rewards\nThe headline rate folds in reward emissions (same composition as `/pairs/optimize`), which is why a borrow rate can be **negative**. That is real but transient, so each row also carries `rewardAprPct` and `aprExRewardsPct` — the structural cost without emissions. Ranking stays on the headline; showing both is the caller's job.\n\n## Ranking\n`obtainable` first, then non-`capped`, then best `effectiveAprPct` (cheapest for a borrow, highest for a supply), then deepest liquidity as tie-break. Results are deduped to one row per `(chain, lender, market, term)` — the pairs view is combinatorial in the other leg, so without it the \"5 best rates\" would be five copies of the same market.","parameters":[{"name":"chainId","in":"query","schema":{"type":"string"},"description":"Single chain ID. When set, asset filters operate on token addresses. Pass one for an actionable list — cross-chain rows are a comparison, not one executable position.","example":"8453"},{"name":"chainIds","in":"query","schema":{"type":"string"},"description":"CSV of chain IDs. When two or more, asset filters operate on asset groups."},{"name":"side","in":"query","schema":{"type":"string","enum":["borrow","supply"],"default":"borrow"},"description":"Which leg to compare."},{"name":"debt","in":"query","schema":{"type":"string"},"description":"The borrowed asset — address (single chain) or asset group. Required for `side=borrow`. Aliases: `debts`, `debtGroups`."},{"name":"debtGroups","in":"query","schema":{"type":"string"},"description":"CSV of debt-side asset groups (works in any chain mode).","example":"USDC"},{"name":"collateral","in":"query","schema":{"type":"string"},"description":"The collateral asset. Required for `side=supply`; strongly recommended for `side=borrow` — a market that will not take your collateral is not a comparable. Aliases: `collaterals`, `collateralGroups`."},{"name":"collateralGroups","in":"query","schema":{"type":"string"},"description":"CSV of collateral-side asset groups (works in any chain mode).","example":"CBBTC"},{"name":"amount","in":"query","schema":{"type":"number"},"description":"Notional in TOKEN UNITS of the priced leg (the debt asset for a borrow). Requires exactly one asset on that side."},{"name":"amountUsd","in":"query","schema":{"type":"number"},"description":"Same notional in USD. Works with multi-asset selections. Mutually exclusive with `amount`."},{"name":"horizonDays","in":"query","schema":{"type":"number","default":365},"description":"Holding period every venue is repriced to. At 365 a floating pool’s effective rate equals its sticker rate."},{"name":"rateType","in":"query","schema":{"type":"string","enum":["all","fixed","float"],"default":"all"},"description":"Post-filter applied before dedupe and ranking."},{"name":"limit","in":"query","schema":{"type":"integer","default":5},"description":"How many comparables to return (max 25)."},{"name":"referenceMarketUid","in":"query","schema":{"type":"string"},"description":"The venue the caller is already on. Returned as `reference` and excluded from `items`, so the pill can render \"you: X% · best: Y%\"."},{"name":"referenceTermId","in":"query","schema":{"type":"string"},"description":"Disambiguates which term of the reference market is the caller’s own."},{"name":"includeUnobtainable","in":"query","schema":{"type":"boolean","default":false},"description":"Keep quotes that cannot be taken right now (closed Term auction rounds), flagged and ranked last."},{"name":"minLiquidityRatio","in":"query","schema":{"type":"number","default":0.5},"description":"Ratio side of the depth floor `min(ratio × anchor, cap)`."},{"name":"liquidityFloorCapUsd","in":"query","schema":{"type":"number","default":10000000},"description":"Cap side of the depth floor — stops a huge anchor from demanding a comparably huge venue and returning nothing."},{"name":"minLiquidityUsd","in":"query","schema":{"type":"number"},"description":"Absolute USD depth floor that REPLACES the computed `min(ratio × anchor, cap)`."},{"name":"includeStale","in":"query","schema":{"type":"boolean","default":false},"description":"Rank venues whose market data is older than the freshness window. Excluded but counted (`droppedStale`) by default."},{"name":"includeIlliquid","in":"query","schema":{"type":"boolean","default":false},"description":"Disable the depth band entirely and rank every venue regardless of size."},{"name":"collateralPreference","in":"query","schema":{"type":"string"},"description":"CSV of collateral asset groups tried in order when picking the comparison basis (only used when no collateral is pinned).","example":"ETH,BTC,USDC,USDT"},{"name":"lenders","in":"query","schema":{"type":"string"},"description":"CSV of lender keys (prefix-expanded) to restrict the comparison to."},{"name":"excludeLenders","in":"query","schema":{"type":"string"},"description":"CSV of lender keys to exclude (prefix-expanded)."},{"name":"maxConfigRiskScore","in":"query","schema":{"type":"number"},"description":"Config-risk ceiling (default 4, as on the pair endpoints)."},{"name":"maxTokenRiskScore","in":"query","schema":{"type":"number"},"description":"Token-risk ceiling across both legs."}],"responses":{"200":{"description":"Comparable rates","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ComparableRatesResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/irm":{"get":{"tags":["Data › Lending"],"summary":"IRM rate curves","description":"Return sampled borrow and deposit rate curves for one or more markets.\n\nEach curve is a series of `(utilization, borrowRate, depositRate)` points spanning\n0% to 100% utilization. Rates are **APR %** (e.g. `5.5` = 5.5% APR).\n\nSupported protocols: `aave`, `aave_v4`, `compound_v2`, `compound_v3`, `morpho` (including Lista), `euler_v2`, `silo`, `fluid`, `gearbox`, `dolomite`.\n\n**Caching:** IRM parameters are quasi-static and cached server-side for 1 hour.\nComputed curves are also cached (keyed by market UIDs + data-point count).","parameters":[{"name":"marketUids","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated market UIDs, e.g. `AAVE_V3:1:0xa0b8...`","example":"SPARK:100:0x2a22f9c3b484c3629090feed35f17ff8f88f76f0"},{"name":"dataPoints","in":"query","schema":{"type":"integer","default":20},"description":"Number of curve sample points (1–20, default 20)","example":20}],"responses":{"200":{"description":"Rate curves per market","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","required":["count","items"],"properties":{"count":{"type":"integer","description":"Number of markets returned"},"items":{"type":"array","items":{"type":"object","required":["marketUid","protocol","lenderKey","chainId","underlyingAddress","points"],"properties":{"marketUid":{"type":"string","description":"Unique market identifier (`lenderKey:chainId:address`)","example":"SPARK:100:0x2a22f9c3b484c3629090feed35f17ff8f88f76f0"},"protocol":{"type":"string","enum":["aave","aave_v4","compound_v2","compound_v3","morpho","euler_v2","silo","fluid","gearbox","dolomite"],"description":"IRM model type"},"lenderKey":{"type":"string","example":"SPARK"},"chainId":{"type":"string","example":"100"},"underlyingAddress":{"type":"string","example":"0x2a22f9c3b484c3629090feed35f17ff8f88f76f0"},"marketName":{"type":"string","example":"Spark USDC.e"},"points":{"type":"array","description":"Sampled rate curve. Length = dataPoints + 1 (includes u=0 and u=1).","items":{"type":"object","required":["utilization","borrowRate","depositRate"],"properties":{"utilization":{"type":"number","minimum":0,"maximum":1,"description":"Utilization ratio (0–1)","example":0.95},"borrowRate":{"type":"number","description":"Variable borrow rate (APR %, e.g. 7.67 = 7.67%)","example":7.67},"depositRate":{"type":"number","description":"Deposit/supply rate (APR %, e.g. 3.30 = 3.30%)","example":3.3}}}}}}}}},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/irm/depth":{"get":{"tags":["Data › Lending"],"summary":"Rate-at-depth (rate vs borrow/supply amount)","description":"Return rate-at-depth data — the amount-axis complement of `/v1/data/lending/irm` (rate vs utilization).\n\nTwo shapes, both optional:\n\n- **grid** (default): a `borrow`/`supply` sweep from 0 to the fillable ceiling — for charts. Disable with `grid=false`.\n- **rateAtAmount**: pass `amount(s)` (token units) and/or `amount(s)Usd` (USD) to get the rate + resulting utilization + fillable ceiling at each specific size — the answer to *\"what rate to borrow X\"*.\n\nFor a utilization pool the whole balance re-prices to one rate, so `rateAtAmount` is the marginal spot rate at the post-action utilization (marginal == average). Order-book venues (Midnight) fill best-first, so their `rateAtAmount` is a **volume-weighted average** over the consumed ladder. Lista's brokered borrow leg is a **flat fixed** rate up to capacity. Rates are **APR %**.\n\n**Caching:** computed grids/points cached server-side for 1 hour (keyed by UIDs + side + points + amounts).","parameters":[{"name":"marketUids","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated market UIDs, e.g. `GEARBOX_V3_0x…:9745:0x…`"},{"name":"side","in":"query","schema":{"type":"string","default":"borrow"},"description":"`borrow` | `supply` | `both` (default `borrow`)","example":"borrow"},{"name":"dataPoints","in":"query","schema":{"type":"integer","default":24},"description":"Samples per grid (1–60, default 24)"},{"name":"amounts","in":"query","schema":{"type":"string"},"description":"Comma-separated sizes in TOKEN units → adds `rateAtAmount[]`. `amount` is accepted as a singular alias.","example":"12000"},{"name":"amountsUsd","in":"query","schema":{"type":"string"},"description":"Comma-separated sizes in USD (converted per market via its price). `amountUsd` alias accepted."},{"name":"grid","in":"query","schema":{"type":"string","default":"true"},"description":"Set `false` to drop the 0→max grid sweep (points-only)."}],"responses":{"200":{"description":"Depth grids and/or rate-at-amount points per market","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","required":["count","items"],"properties":{"count":{"type":"integer"},"items":{"type":"array","items":{"type":"object","required":["marketUid","lenderKey","chainId"],"properties":{"marketUid":{"type":"string"},"protocol":{"type":"string"},"lenderKey":{"type":"string"},"chainId":{"type":"string"},"underlyingAddress":{"type":"string"},"utilization":{"type":"number","nullable":true,"description":"Current utilization (0–1)"},"variableBorrowRate":{"type":"number","nullable":true,"description":"Current 0-notional borrow APR %"},"borrow":{"type":"object","description":"Rate-vs-amount sweep from size 0 to `fillable`.","properties":{"side":{"type":"string","enum":["borrow","supply"]},"currentUtilization":{"type":"number"},"currentBorrowAprPct":{"type":"number"},"currentDepositAprPct":{"type":"number"},"fillable":{"type":"number","description":"Largest size sampled — the depth ceiling (token units)."},"fillableUsd":{"type":"number","nullable":true},"points":{"type":"array","items":{"type":"object","properties":{"size":{"type":"number","description":"Cumulative size (token units)"},"sizeUsd":{"type":"number","nullable":true},"utilization":{"type":"number"},"borrowAprPct":{"type":"number"},"depositAprPct":{"type":"number"}}}}}},"supply":{"type":"object","description":"Rate-vs-amount sweep from size 0 to `fillable`.","properties":{"side":{"type":"string","enum":["borrow","supply"]},"currentUtilization":{"type":"number"},"currentBorrowAprPct":{"type":"number"},"currentDepositAprPct":{"type":"number"},"fillable":{"type":"number","description":"Largest size sampled — the depth ceiling (token units)."},"fillableUsd":{"type":"number","nullable":true},"points":{"type":"array","items":{"type":"object","properties":{"size":{"type":"number","description":"Cumulative size (token units)"},"sizeUsd":{"type":"number","nullable":true},"utilization":{"type":"number"},"borrowAprPct":{"type":"number"},"depositAprPct":{"type":"number"}}}}}},"rateAtAmount":{"type":"array","description":"Rate + fillable at each requested size (present only when `amount(s)`/`amount(s)Usd` is passed).","items":{"type":"object","required":["side","size","borrowAprPct","depositAprPct","fillable","capped"],"properties":{"side":{"type":"string","enum":["borrow","supply"]},"size":{"type":"number","description":"Requested size (token units)"},"amountUsd":{"type":"number","nullable":true},"utilization":{"type":"number","description":"Post-action utilization (0 for order books)"},"borrowAprPct":{"type":"number"},"depositAprPct":{"type":"number"},"fillable":{"type":"number","description":"Max borrowable/suppliable before a cap binds (token units)"},"capped":{"type":"boolean","description":"True when `size` exceeds `fillable` — rate is reported at the ceiling."}}}}}}}}},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/mode":{"get":{"tags":["Data › Lending"],"summary":"List mode categories","description":"Return the available risk-category (\"mode\") entries for the requested lenders and chains.\n\nEach entry contains the lender key, chain ID, and a list of mode categories (id + label) under the `eModes` field.\n\nThe \"mode\" terminology is the protocol-agnostic generalization of Aave V3's \"e-mode\" (efficiency mode); other lenders expose analogous category mechanisms.\n\nResults are cached server-side (1 hour TTL) because mode categories rarely change.","parameters":[{"name":"lenders","in":"query","required":true,"description":"Protocol identifiers (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":"AAVE_V3"},{"name":"chains","in":"query","required":true,"description":"Chain IDs (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":"1"}],"responses":{"200":{"description":"Mode categories per lender/chain","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","additionalProperties":true},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/mode/analysis":{"get":{"tags":["Data › Lending"],"summary":"Mode analysis (on-chain)","description":"Evaluate all mode-switching options for a user. The API fetches the user's current positions and balance data on-chain via multicall — no request body needed.\n\n**Output:** For each available mode on the specified lender/chain, returns:\n- **healthFactor** — the hypothetical health factor if the user switches to that mode (`null` if no debt)\n- **supportedAssets** — which marketUids are eligible as collateral or for borrowing in that mode\n- **canSwitch** — whether the switch is safe (health factor > 1 and no incompatible debt)\n\nMode categories are cached server-side (1 hour TTL). Market configs are cached (3 minute TTL).\n\nThe \"mode\" terminology is the protocol-agnostic generalization of Aave V3's \"e-mode\" (efficiency mode); other lenders expose analogous category mechanisms.","parameters":[{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"Protocol identifier","example":"AAVE_V3"},{"name":"chain","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"Wallet address of the user","example":"0xYourAddress"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Sub-account ID (for multi-subaccount lenders like Init Capital). Defaults to first sub-account."}],"responses":{"200":{"description":"Mode analysis results","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","additionalProperties":true},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Data › Lending"],"summary":"Mode analysis (with body)","description":"Evaluate all mode-switching options for a user sub-account.\n\n**Input:** POST a `UserDataForSubAccount` object (the same shape returned by `/user-positions` per sub-account).\n\n**Output:** For each available mode on the specified lender/chain, returns:\n- **healthFactor** — the hypothetical health factor if the user switches to that mode (`null` if no debt)\n- **supportedAssets** — which marketUids are eligible as collateral or for borrowing in that mode\n- **canSwitch** — whether the switch is safe (health factor > 1 and no incompatible debt)\n\nMode categories are cached server-side (1 hour TTL). Market configs are cached (3 minute TTL).\n\nThe \"mode\" terminology is the protocol-agnostic generalization of Aave V3's \"e-mode\" (efficiency mode); other lenders expose analogous category mechanisms.","parameters":[{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"Protocol identifier","example":"AAVE_V3"},{"name":"chain","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"}],"requestBody":{"required":true,"description":"A `UserDataForSubAccount` object. Validation returns specific field-level errors (e.g. \"missing field: balanceData.collateral (number)\").","content":{"application/json":{"schema":{"type":"object","required":["balanceData","positions","userConfig"],"properties":{"accountId":{"type":"string"},"health":{"type":"number","nullable":true},"borrowCapacityUSD":{"type":"number"},"balanceData":{"type":"object","required":["collateral","adjustedDebt"],"description":"Balance data for the sub-account. `collateral` and `adjustedDebt` are required for health factor calculation.","properties":{"collateral":{"type":"number"},"adjustedDebt":{"type":"number"},"deposits":{"type":"number"},"debt":{"type":"number"},"borrowDiscountedCollateral":{"type":"number"},"nav":{"type":"number"}}},"aprData":{"type":"object"},"positions":{"type":"array","items":{"type":"object","properties":{"marketUid":{"type":"string"},"depositsUSD":{"type":"number"},"debtUSD":{"type":"number"},"debtStableUSD":{"type":"number"},"collateralEnabled":{"type":"boolean"}}}},"userConfig":{"type":"object","required":["selectedMode"],"properties":{"selectedMode":{"type":"string","description":"Current mode/config key"},"id":{"type":"string"},"isWhitelisted":{"type":"boolean"}}}}}}}},"responses":{"200":{"description":"Mode analysis results","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","additionalProperties":true},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/next-account":{"get":{"tags":["Data › Lending"],"summary":"Next available sub-account","description":"Discover the next available sub-account or position ID for a given lender and owner.\n\nDifferent protocols handle sub-accounts differently:\n\n- **Euler V2** (`accountType: SELECT`): Sub-accounts are derived by XORing the owner's last address byte with an index (0-255). The integrator selects an `accountId` from the range. Any unused value automatically creates a new sub-account.\n- **Dolomite** (`accountType: SELECT`): Margin sub-accounts are arbitrary `uint256` account numbers (`accountIdRange` is `0` to `2^256-1`). Account `0` is the default cross-margin account; `activeAccountIds` lists funded accounts and `nextAccountId` is the lowest unused integer. Any unused number automatically creates a new sub-account.\n- **Init Capital** (`accountType: AUTOGEN`): Position IDs are NFT hashes generated on-chain. To create a new position, **omit** the `posId`/`accountId` parameter entirely. The `nextAccountId` field is a preview of the ID that will be generated.\n- **Other lenders** (Aave, Morpho, Compound, etc.): Do not support sub-accounts — returns a 400 error.\n\n### Integration Guide\n\n| `accountType` | To create new account | To use existing account |\n|---|---|---|\n| `SELECT` | Pass `accountId=<unused value>` from `accountIdRange` | Pass `accountId=<existing value>` |\n| `AUTOGEN` | **Omit** `accountId` / `posId` parameter | Pass `posId=<existing ID>` |","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID.","example":"1"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"Lender identifier.","example":"EULER_V2"},{"name":"account","in":"query","required":true,"schema":{"type":"string"},"description":"Owner wallet address.","example":"0xYourAddress"}],"responses":{"200":{"description":"Next account information","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/NextAccountResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/orders":{"get":{"tags":["Data › Lending"],"summary":"Your open orders (unified)","operationId":"lending-orders","description":"A user's own cancellable/pending orders on an order-book or auction lender, normalized into ONE shape:\n- `MORPHO_MIDNIGHT_<id>` → signed maker limit offers (`kind: 'maker-offer'`), cancel via [`/v1/actions/midnight/cancel`](#operation/midnight-cancel).\n- `TERM_FINANCE_<id>` → secondary repo-token listings (`kind: 'listing'`) + primary sealed-bid auction submissions (`kind: 'auction-offer'|'auction-bid'`), managed via [`/v1/actions/term/*`](#operation/term-offer).\n\nEach order carries a self-describing `cancel` action (`{ method, path, query }`) pointing at the exact endpoint, so one Cancel button works for every provider. `aprPct` is `null` while an auction price is still sealed. To TAKE (fill) *other* users' liquidity, use the standard lending actions + the unified ladder at [`/v1/data/lending/book`](#operation/lending-book).","parameters":[{"name":"marketUid","in":"query","schema":{"type":"string"},"description":"Market uid `lender:chainId:asset` (or pass `lender` + `chainId`).","example":"TERM_FINANCE_0xABC…:1:0xA0b8…"},{"name":"lender","in":"query","schema":{"type":"string"},"description":"`MORPHO_MIDNIGHT_<id>` or `TERM_FINANCE_<id>` lender key.","example":"TERM_FINANCE_0xABC…"},{"name":"chainId","in":"query","schema":{"type":"string"},"description":"Chain ID (aliases: `chains`, `chain`).","example":"1"},{"name":"account","in":"query","required":true,"schema":{"type":"string"},"description":"The order owner whose orders to return."}],"responses":{"200":{"description":"The account's own orders on the market","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/UnifiedOrdersResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/loop/range/leverage":{"get":{"tags":["Data › Loop"],"summary":"Max leverage range","description":"Compute the maximum amount that can be opened in a leverage position.\n\n**Single-pair mode** (provide `marketUidIn` + `marketUidOut`): returns the max open amount for one specific pair.\n\n**Multi-pair mode** (provide `lender` + `chainId`): returns the max open amount for all leverage pairs of a lender, optionally filtered by `marketUidIn` / `marketUidOut`.\n\n**Zap mode**: If `payAmount` and `payPriceUSD` are provided, the calculation includes the deposited amount as additional collateral before computing the leverage range.\n\nGET fetches user balances on-chain (requires `account`). POST accepts a `SimulationBody` in the request body.","parameters":[{"name":"marketUidIn","in":"query","schema":{"type":"string"},"description":"Market identifier for the input (debt/short) side.","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","schema":{"type":"string"},"description":"Market identifier for the output (collateral/long) side.","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"account","in":"query","schema":{"type":"string"},"description":"Wallet address. Required for GET (on-chain balance fetch).","example":"0xYourAddress"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Sub-account ID (e.g. Euler V2, Init Capital)."},{"name":"lender","in":"query","schema":{"type":"string"},"description":"Lender identifier. Required for multi-pair mode.","example":"AAVE_V3"},{"name":"chainId","in":"query","schema":{"type":"string"},"description":"Chain ID. Required for multi-pair mode.","example":"1"},{"name":"payAmount","in":"query","schema":{"type":"string"},"description":"Amount of the pay asset to deposit (for zap calculation). Triggers zap mode when present."},{"name":"payPriceUSD","in":"query","schema":{"type":"string"},"description":"USD price of the pay asset. If omitted, defaults to the long (collateral) asset price from the pair."}],"responses":{"200":{"description":"Max open range for one or more leverage pairs","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","description":"Array of range results.","items":{"$ref":"#/components/schemas/RangeResult"}},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Data › Loop"],"summary":"Max leverage range (with body)","description":"Compute the maximum amount that can be opened in a leverage position.\n\n**Single-pair mode** (provide `marketUidIn` + `marketUidOut`): returns the max open amount for one specific pair.\n\n**Multi-pair mode** (provide `lender` + `chainId`): returns the max open amount for all leverage pairs of a lender, optionally filtered by `marketUidIn` / `marketUidOut`.\n\n**Zap mode**: If `payAmount` and `payPriceUSD` are provided, the calculation includes the deposited amount as additional collateral before computing the leverage range.\n\nGET fetches user balances on-chain (requires `account`). POST accepts a `SimulationBody` in the request body.\n\nPOST accepts a JSON body with the user's current portfolio state, avoiding an on-chain fetch.","parameters":[{"name":"marketUidIn","in":"query","schema":{"type":"string"},"description":"Market identifier for the input (debt/short) side.","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","schema":{"type":"string"},"description":"Market identifier for the output (collateral/long) side.","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"account","in":"query","schema":{"type":"string"},"description":"Wallet address. Required for GET (on-chain balance fetch).","example":"0xYourAddress"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Sub-account ID (e.g. Euler V2, Init Capital)."},{"name":"lender","in":"query","schema":{"type":"string"},"description":"Lender identifier. Required for multi-pair mode.","example":"AAVE_V3"},{"name":"chainId","in":"query","schema":{"type":"string"},"description":"Chain ID. Required for multi-pair mode.","example":"1"},{"name":"payAmount","in":"query","schema":{"type":"string"},"description":"Amount of the pay asset to deposit (for zap calculation). Triggers zap mode when present."},{"name":"payPriceUSD","in":"query","schema":{"type":"string"},"description":"USD price of the pay asset. If omitted, defaults to the long (collateral) asset price from the pair."}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. If omitted, the API fetches balances on-chain automatically (slower, no `simulation` in response). When provided, pass `balanceData`, `aprData`, and `positions` directly from the matching sub-account in the `/v1/data/lending/user-positions` response — see the `SimulationBody` schema for a step-by-step example.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationBody"}}}},"responses":{"200":{"description":"Max open range for one or more leverage pairs","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","description":"Array of range results.","items":{"$ref":"#/components/schemas/RangeResult"}},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/loop/range/collateral-swap":{"get":{"tags":["Data › Loop"],"summary":"Max collateral swap range","description":"Compute the maximum amount for a collateral swap.\n\nThe max swappable amount equals the user's collateral deposit in the source (input) asset.\n\nRequires `positions[]` in the POST body (or `account` for GET) to read the user's collateral balances.","parameters":[{"name":"marketUidIn","in":"query","schema":{"type":"string"},"description":"Market identifier for the input (debt/short) side.","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","schema":{"type":"string"},"description":"Market identifier for the output (collateral/long) side.","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"account","in":"query","schema":{"type":"string"},"description":"Wallet address. Required for GET (on-chain balance fetch).","example":"0xYourAddress"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Sub-account ID (e.g. Euler V2, Init Capital)."}],"responses":{"200":{"description":"Max collateral swap range","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","description":"Array of range results.","items":{"$ref":"#/components/schemas/CollateralSwapRangeResult"}},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Data › Loop"],"summary":"Max collateral swap range (with body)","description":"Compute the maximum amount for a collateral swap.\n\nThe max swappable amount equals the user's collateral deposit in the source (input) asset.\n\nRequires `positions[]` in the POST body (or `account` for GET) to read the user's collateral balances.\n\nPOST accepts a JSON body with the user's current portfolio state, avoiding an on-chain fetch.","parameters":[{"name":"marketUidIn","in":"query","schema":{"type":"string"},"description":"Market identifier for the input (debt/short) side.","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","schema":{"type":"string"},"description":"Market identifier for the output (collateral/long) side.","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"account","in":"query","schema":{"type":"string"},"description":"Wallet address. Required for GET (on-chain balance fetch).","example":"0xYourAddress"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Sub-account ID (e.g. Euler V2, Init Capital)."}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. If omitted, the API fetches balances on-chain automatically (slower, no `simulation` in response). When provided, pass `balanceData`, `aprData`, and `positions` directly from the matching sub-account in the `/v1/data/lending/user-positions` response — see the `SimulationBody` schema for a step-by-step example.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationBody"}}}},"responses":{"200":{"description":"Max collateral swap range","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","description":"Array of range results.","items":{"$ref":"#/components/schemas/CollateralSwapRangeResult"}},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/loop/range/debt-swap":{"get":{"tags":["Data › Loop"],"summary":"Max debt swap range","description":"Compute the maximum amount for a debt swap.\n\nThe max swappable amount equals the user's total debt (variable + stable) in the source (input) asset.\n\nUse `denomination=exactInput` (default) to base the range on the source debt, or `exactOutput` to base it on the target debt.\n\nRequires `positions[]` in the POST body (or `account` for GET) to read the user's debt balances.","parameters":[{"name":"marketUidIn","in":"query","schema":{"type":"string"},"description":"Market identifier for the input (debt/short) side.","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","schema":{"type":"string"},"description":"Market identifier for the output (collateral/long) side.","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"account","in":"query","schema":{"type":"string"},"description":"Wallet address. Required for GET (on-chain balance fetch).","example":"0xYourAddress"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Sub-account ID (e.g. Euler V2, Init Capital)."},{"name":"denomination","in":"query","schema":{"type":"string","enum":["exactInput","exactOutput"],"default":"exactInput"},"description":"Which side is the base denomination for the range."}],"responses":{"200":{"description":"Max debt swap range","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","description":"Array of range results.","items":{"$ref":"#/components/schemas/DebtSwapRangeResult"}},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Data › Loop"],"summary":"Max debt swap range (with body)","description":"Compute the maximum amount for a debt swap.\n\nThe max swappable amount equals the user's total debt (variable + stable) in the source (input) asset.\n\nUse `denomination=exactInput` (default) to base the range on the source debt, or `exactOutput` to base it on the target debt.\n\nRequires `positions[]` in the POST body (or `account` for GET) to read the user's debt balances.\n\nPOST accepts a JSON body with the user's current portfolio state, avoiding an on-chain fetch.","parameters":[{"name":"marketUidIn","in":"query","schema":{"type":"string"},"description":"Market identifier for the input (debt/short) side.","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","schema":{"type":"string"},"description":"Market identifier for the output (collateral/long) side.","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"account","in":"query","schema":{"type":"string"},"description":"Wallet address. Required for GET (on-chain balance fetch).","example":"0xYourAddress"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Sub-account ID (e.g. Euler V2, Init Capital)."},{"name":"denomination","in":"query","schema":{"type":"string","enum":["exactInput","exactOutput"],"default":"exactInput"},"description":"Which side is the base denomination for the range."}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. If omitted, the API fetches balances on-chain automatically (slower, no `simulation` in response). When provided, pass `balanceData`, `aprData`, and `positions` directly from the matching sub-account in the `/v1/data/lending/user-positions` response — see the `SimulationBody` schema for a step-by-step example.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationBody"}}}},"responses":{"200":{"description":"Max debt swap range","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","description":"Array of range results.","items":{"$ref":"#/components/schemas/DebtSwapRangeResult"}},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/loop/range/close":{"get":{"tags":["Data › Loop"],"summary":"Max close range","description":"Compute the maximum amount for closing (deleveraging) a position.\n\nThe max closeable amount is bounded by the smaller of the user's collateral and debt. `amountIn` = collateral withdrawn, `amountOut` = debt repaid.\n\nUse `denomination=exactInput` (default) to base the range on collateral, or `exactOutput` to base it on debt.\n\nRequires `positions[]` in the POST body (or `account` for GET) to read collateral and debt balances.","parameters":[{"name":"marketUidIn","in":"query","schema":{"type":"string"},"description":"Market identifier for the input (debt/short) side.","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","schema":{"type":"string"},"description":"Market identifier for the output (collateral/long) side.","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"account","in":"query","schema":{"type":"string"},"description":"Wallet address. Required for GET (on-chain balance fetch).","example":"0xYourAddress"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Sub-account ID (e.g. Euler V2, Init Capital)."},{"name":"denomination","in":"query","schema":{"type":"string","enum":["exactInput","exactOutput"],"default":"exactInput"},"description":"Which side is the base denomination for the range."}],"responses":{"200":{"description":"Max close range","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","description":"Array of range results.","items":{"$ref":"#/components/schemas/CloseRangeResult"}},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Data › Loop"],"summary":"Max close range (with body)","description":"Compute the maximum amount for closing (deleveraging) a position.\n\nThe max closeable amount is bounded by the smaller of the user's collateral and debt. `amountIn` = collateral withdrawn, `amountOut` = debt repaid.\n\nUse `denomination=exactInput` (default) to base the range on collateral, or `exactOutput` to base it on debt.\n\nRequires `positions[]` in the POST body (or `account` for GET) to read collateral and debt balances.\n\nPOST accepts a JSON body with the user's current portfolio state, avoiding an on-chain fetch.","parameters":[{"name":"marketUidIn","in":"query","schema":{"type":"string"},"description":"Market identifier for the input (debt/short) side.","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","schema":{"type":"string"},"description":"Market identifier for the output (collateral/long) side.","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"account","in":"query","schema":{"type":"string"},"description":"Wallet address. Required for GET (on-chain balance fetch).","example":"0xYourAddress"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Sub-account ID (e.g. Euler V2, Init Capital)."},{"name":"denomination","in":"query","schema":{"type":"string","enum":["exactInput","exactOutput"],"default":"exactInput"},"description":"Which side is the base denomination for the range."}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. If omitted, the API fetches balances on-chain automatically (slower, no `simulation` in response). When provided, pass `balanceData`, `aprData`, and `positions` directly from the matching sub-account in the `/v1/data/lending/user-positions` response — see the `SimulationBody` schema for a step-by-step example.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationBody"}}}},"responses":{"200":{"description":"Max close range","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","description":"Array of range results.","items":{"$ref":"#/components/schemas/CloseRangeResult"}},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/yields/intrinsic/latest":{"get":{"tags":["Data › Yields"],"summary":"Get latest intrinsic yields","description":"Returns latest intrinsic yield (APR) for each asset group.","parameters":[{"name":"assets","in":"query","description":"Filter by asset group keys (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["ETH","USDC"]},{"name":"asOf","in":"query","schema":{"type":"string"},"description":"ISO date anchor; defaults to latest hour","example":"2025-01-15T12:00:00Z"}],"responses":{"200":{"description":"Intrinsic yield data","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/IntrinsicYieldResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/lending/yields/intrinsic/snapshots":{"get":{"tags":["Data › Yields"],"summary":"Get intrinsic yield snapshots","description":"Returns historical intrinsic yield time series per asset group.","parameters":[{"name":"assetGroups","in":"query","required":true,"description":"Asset group keys (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["ETH","USDC"]},{"name":"start","in":"query","schema":{"type":"string"},"description":"ISO start date","example":"2025-11-01T00:00:00Z"},{"name":"end","in":"query","schema":{"type":"string"},"description":"ISO end date","example":"2025-11-15T00:00:00Z"}],"responses":{"200":{"description":"Intrinsic yield snapshots","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/IntrinsicYieldSnapshotResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/lending/yields/by-asset/latest":{"get":{"tags":["Data › Yields"],"summary":"Get latest yields by asset","description":"Returns latest lending yields grouped by asset for a given chain.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"assets","in":"query","description":"Filter by asset group keys (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["ETH","USDC"]},{"name":"lenders","in":"query","description":"Filter by lender keys (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["AAVE_V3","COMPOUND_V3_USDC"]}],"responses":{"200":{"description":"Yield data by asset","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LenderYieldResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/lending/yields/by-asset/snapshots":{"get":{"tags":["Data › Yields"],"summary":"Get yield snapshots by asset","description":"Returns historical yield time series grouped by asset for a given chain.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"assets","in":"query","description":"Filter by asset group keys (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["ETH","USDC"]},{"name":"lenders","in":"query","description":"Filter by lender keys (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["AAVE_V3","COMPOUND_V3_USDC"]},{"name":"start","in":"query","schema":{"type":"string"},"description":"ISO start date","example":"2025-11-01T00:00:00Z"},{"name":"end","in":"query","schema":{"type":"string"},"description":"ISO end date","example":"2025-11-15T00:00:00Z"}],"responses":{"200":{"description":"Yield snapshot data by asset","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LenderYieldResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/lending/yields/by-lender/latest":{"get":{"tags":["Data › Yields"],"summary":"Get latest yields by lender","description":"Returns latest lending yields grouped by protocol.","parameters":[{"name":"chainIds","in":"query","description":"Filter by chain IDs (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["1","42161"]},{"name":"lenders","in":"query","description":"Filter by lender keys (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["AAVE_V3","COMPOUND_V3_USDC"]}],"responses":{"200":{"description":"Yield data by lender","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LenderYieldResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/lending/yields/by-lender/snapshots":{"get":{"tags":["Data › Yields"],"summary":"Get yield snapshots by lender","description":"Returns historical yield time series grouped by protocol.","parameters":[{"name":"chainIds","in":"query","description":"Filter by chain IDs (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["1","42161"]},{"name":"lenders","in":"query","description":"Filter by lender keys (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["AAVE_V3","COMPOUND_V3_USDC"]},{"name":"start","in":"query","schema":{"type":"string"},"description":"ISO start date","example":"2025-11-01T00:00:00Z"},{"name":"end","in":"query","schema":{"type":"string"},"description":"ISO end date","example":"2025-11-15T00:00:00Z"}],"responses":{"200":{"description":"Yield snapshot data by lender","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LenderYieldResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/lending/user-positions":{"get":{"tags":["Data › User Positions"],"summary":"User positions","description":"Fetches lending and borrowing positions for a given account across one or more chains.\n\n**Response Structure:**\n- `items`: Flat array of lender entries sorted by net worth (descending). Each entry fuses sub-account position data with aggregated summary metrics (deposits, debt, APRs, health, leverage).\n- `summary`: Portfolio-wide totals (net worth, APRs, leverage, active counts) plus per-chain breakdowns.\n- `partial` / `incompleteLenders`: only present when some lender could not be read in full (RPC error or reverted call). A lender whose reads all failed is omitted rather than returned as an empty position, so totals are a lower bound for that request.\n\n**Lender Entry includes:**\n- Per-asset deposits and debt (in USD and token units)\n- Sub-account data with health factors and borrow capacity\n- Aggregated lender-level metrics (total deposits/debt, weighted APRs, health factor, leverage)\n- Collateral status, withdrawable/borrowable amounts per position\n\n**Portfolio Summary includes:**\n- Total deposits, debt, and net worth (current + 24h ago) across all chains\n- Weighted average APRs and overall leverage ratio\n- Count of active lenders and chains\n- Per-chain totals (deposits, debt, net worth, lender count)\n\nThe server executes RPC calls internally and returns fully parsed results.\n\n---\n\n**Using with action endpoints (POST simulation):**\n\nAll action endpoints (`/v1/actions/lending/*`, `/v1/actions/loop/*`) accept an optional POST body for post-trade simulation. The data comes directly from this endpoint:\n\n```\n// 1. Fetch positions\nGET /v1/data/lending/user-positions?account=0x...&chains=1\n\n// 2. Pick the lender entry + sub-account you're acting on\nconst sub = response.data.items[i].data[j]\n\n// 3. POST to any action endpoint with the same query params as GET, plus:\nPOST /v1/actions/lending/deposit?marketUid=AAVE_V3:1:0x...&amount=1000000\n{\n  \"balanceData\": sub.balanceData,\n  \"aprData\": sub.aprData,\n  \"positions\": sub.positions,\n  \"modeId\": sub.userConfig.selectedMode\n}\n\n// 4. Response includes a \"simulation\" field with pre/post health factor,\n//    borrow capacity, and projected balanceData/aprData.\n```\n\nSee the `SimulationBody` schema for full details.","parameters":[{"name":"account","in":"query","required":true,"schema":{"type":"string"},"description":"EVM account address (0x-prefixed, 40 hex chars)","example":"0xbadA9c382165b31419F4CC0eDf0Fa84f80A3C8E5"},{"name":"chains","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated chain IDs","example":"1,42161"},{"name":"lenders","in":"query","schema":{"type":"string"},"description":"Comma-separated lender IDs to filter by. If omitted, all supported lenders for each chain are included.","example":"AAVE_V3,COMPOUND_V3_WETH"}],"responses":{"200":{"description":"User position data with portfolio summary","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/UserPositionResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/user-positions/rpc-call":{"get":{"tags":["Data › User Positions"],"summary":"User position RPC calls","description":"Prepares raw JSON-RPC calls (using multicall3 aggregate3) for fetching user lending positions on a single chain. Returns an `rpcCallId` and an array of `rpcCalls` that the integrator executes against their own RPC provider. The raw responses are then submitted to `/parse` together with the `rpcCallId`.","parameters":[{"name":"account","in":"query","required":true,"schema":{"type":"string"},"description":"EVM account address (0x-prefixed, 40 hex chars)","example":"0xbadA9c382165b31419F4CC0eDf0Fa84f80A3C8E5"},{"name":"chain","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID to query","example":"1"},{"name":"lenders","in":"query","schema":{"type":"string"},"description":"Comma-separated lender IDs to filter by. If omitted, all supported lenders for the chain are included.","example":"AAVE_V3,COMPOUND_V3_WETH"},{"name":"batchSize","in":"query","schema":{"type":"integer","default":4096},"description":"Max number of sub-calls per multicall batch","example":4096},{"name":"blockTag","in":"query","schema":{"type":"string","default":"latest"},"description":"Block tag for the RPC calls","example":"latest"}],"responses":{"200":{"description":"Prepared RPC calls with a context ID for parsing","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RpcCallResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/lending/user-positions/parse":{"post":{"tags":["Data › User Positions"],"summary":"Parse user positions","description":"Accepts the raw multicall RPC responses obtained by executing the calls from `/rpc-call` and decodes them into structured user position data.\n\n**Workflow:**\n1. Call `/rpc-call` to get prepared RPC calls and a `rpcCallId`\n2. Execute the RPC calls against your own node\n3. Send `rpcCallId` + `rawResponses` to this endpoint for parsing\n\nThe `rpcCallId` ties the responses back to the cached preparation context (valid for 5 minutes).\nAfter successful parsing the cached context is deleted.\n\n**Response Structure:**\n- `items`: Flat array of lender entries sorted by net worth (descending). Each entry fuses sub-account position data with aggregated summary metrics (deposits, debt, APRs, health, leverage).\n- `summary`: Portfolio-wide totals (net worth, APRs, leverage, active counts) plus per-chain breakdowns.\n\n**Lender Entry includes:**\n- Per-asset deposits and debt (in USD and token units)\n- Sub-account data with health factors and borrow capacity\n- Aggregated lender-level metrics (total deposits/debt, weighted APRs, health factor, leverage)\n- Collateral status, withdrawable/borrowable amounts per position\n\n**Portfolio Summary includes:**\n- Total deposits, debt, and net worth (current + 24h ago) across all chains\n- Weighted average APRs and overall leverage ratio\n- Count of active lenders and chains\n- Per-chain totals (deposits, debt, net worth, lender count)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseUserDataRequest"}}}},"responses":{"200":{"description":"Parsed user position data with portfolio summary","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/UserPositionResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"RPC call context not found or expired (older than 5 minutes). Call `/rpc-call` again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/prices/latest":{"get":{"tags":["Data › Prices"],"summary":"Get latest prices","description":"Returns latest USD prices for tracked assets.","parameters":[{"name":"assets","in":"query","description":"Asset group keys (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["ETH","USDC"]},{"name":"asOf","in":"query","schema":{"type":"string"},"description":"ISO date anchor; defaults to latest hour","example":"2025-01-15T12:00:00Z"}],"responses":{"200":{"description":"Latest price data","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/PriceResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/prices/latest-asof":{"get":{"tags":["Data › Prices"],"summary":"Get prices as of X hours ago","description":"Returns prices at an effective cutoff time computed as `asOf − hoursAgo`.","parameters":[{"name":"hoursAgo","in":"query","required":true,"schema":{"type":"number"},"description":"Non-negative number of hours in the past","example":24},{"name":"asOf","in":"query","schema":{"type":"string"},"description":"ISO date anchor; defaults to now","example":"2025-01-15T12:00:00Z"},{"name":"assetGroups","in":"query","description":"Asset group keys (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["ETH","USDC"]}],"responses":{"200":{"description":"Prices at the effective cutoff time","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/PriceResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/health":{"get":{"tags":["Data › General"],"summary":"Health check","description":"Returns service health status.","responses":{"200":{"description":"Service health","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/HealthResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/rpcs":{"get":{"tags":["Data › General"],"summary":"Get RPC endpoints for chains","description":"Returns RPC URLs for the requested chains.","parameters":[{"name":"chains","in":"query","required":true,"description":"Chain IDs to fetch RPCs for (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["1","10"]}],"responses":{"200":{"description":"RPC endpoints for requested chains","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RpcsResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"None of the requested chains exist in the data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/chains":{"get":{"tags":["Data › General"],"summary":"Get supported chains","description":"Returns supported chains as `{ chainId, name, logoURI }` objects, sorted ascending by numeric chainId. Frontends should consume this instead of resolving names/logos client-side.","responses":{"200":{"description":"Supported chains","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ChainsResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/lender-ids":{"get":{"tags":["Data › General"],"summary":"Get lender IDs","description":"Returns the global list of supported lender protocol identifiers (UPPER_SNAKE_CASE keys), independent of chain. Use these as `lender` / `lenders` parameter values across the API.","responses":{"200":{"description":"Lender IDs","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LenderIdsResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/snapshots":{"get":{"tags":["Data › Lending"],"summary":"Get lending snapshots","description":"Returns historical lending data time series for specified markets.","parameters":[{"name":"marketUids","in":"query","required":true,"description":"Market UIDs (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"]},{"name":"fields","in":"query","description":"Fields to include in each snapshot point (repeatable)","example":["depositRate","variableBorrowRate"],"schema":{"type":"array","items":{"type":"string","enum":["depositRate","variableBorrowRate","stableBorrowRate","totalDeposits","totalDebtStable","totalDebt","totalLiquidity","totalDepositsUsd","totalDebtStableUsd","totalDebtUsd","totalLiquidityUsd"]}},"style":"form","explode":true},{"name":"start","in":"query","schema":{"type":"string"},"description":"ISO start date","example":"2025-11-01T00:00:00Z"},{"name":"end","in":"query","schema":{"type":"string"},"description":"ISO end date","example":"2025-11-15T00:00:00Z"}],"responses":{"200":{"description":"Lending snapshot data","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LendingSnapshotResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/sparklines":{"post":{"tags":["Data › Prices"],"summary":"Sparkline price-ratio time series","description":"Returns price-ratio time series for the cross product of `currencies` × `quotes` over a configurable lookback window (default 24h).\n\n**Request body:** `{ currencies: string[], quotes: string[], windowHours?: number }` where each ID is either a shorthand (`\"usd\"`, `\"eth\"`) or `\"{chainId}-{address}\"`. See the `SparklineRequest` schema.\n\n**Response:** `{ windowHours, count, result }`. Each `result[i]` has `{ currency, quote, data }` where `data` is an array of `{ time, value }` points and `value = price(currency) / price(quote)`. Pairs with no data are omitted from `result` (so `count` ≤ `currencies.length × quotes.length`).\n\nUseful for rendering small inline charts (token cards, asset selectors) without hitting the heavier snapshot endpoints.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SparklineRequest"}}}},"responses":{"200":{"description":"Sparkline data","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SparklineResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/meta/lending/complete":{"get":{"tags":["Data › General"],"summary":"Complete lending metadata","description":"Returns the full lending-protocol metadata bundle for the requested chains and lenders. This is the single source of truth for static lender/chain/market configuration consumed by the SDKs and the position-builder UI.\n\n**Response includes**, per `(chainId, lenderKey)`:\n- Supported markets and their underlying assets (address, symbol, decimals, asset group)\n- Per-market risk parameters (LTV, liquidation threshold, factor, mode/e-mode IDs)\n- Pool configuration IDs and which markets are eligible as collateral / borrowable per config\n- Protocol contract addresses (pool, oracle, IRM, composer wiring)\n\nFilter with `chainIds` and/or `lenders` to keep payloads small — both filters are AND-combined.","parameters":[{"name":"chainIds","in":"query","description":"Filter by chain IDs (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["1","42161"]},{"name":"lenders","in":"query","description":"Filter by lender keys (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["AAVE_V3","COMPOUND_V3_USDC"]}],"responses":{"200":{"description":"Lending metadata","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/MetaLendingCompleteResponse"},"actions":{"type":"null"}}}}}}}}},"/v1/data/lending/isolated-markets/meta":{"get":{"tags":["Data › Lending"],"summary":"Get isolated market metadata","description":"Returns Morpho isolated market metadata including fee, LLTV, oracle, IRM, and listing status for all markets, optionally filtered by chain.","parameters":[{"name":"chainIds","in":"query","description":"Filter by chain IDs (repeatable)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"example":["1","8453"]}],"responses":{"200":{"description":"Isolated market metadata","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","additionalProperties":true},"actions":{"type":"null"}}}}}}}}},"/v1/data/token/list":{"get":{"tags":["Data › Token"],"summary":"Get token list for a chain","description":"Fetches the complete token list for a given chain, including metadata such as symbol, name, decimals, and asset group.\n\n**Response Structure:**\n- `tokens`: Object keyed by token address (lowercase) containing token metadata\n- `count`: Total number of tokens in the list\n\n**Token Metadata includes:**\n- `symbol`: Token symbol (e.g., \"USDC\")\n- `name`: Full token name (e.g., \"USD Coin\")\n- `decimals`: Token decimals (e.g., 6 for USDC)\n- `assetGroup`: Asset grouping for price lookups\n- `logoURI`: Optional logo URL","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID to fetch token list for","example":"1"}],"responses":{"200":{"description":"Token list for the chain","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/TokenListResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/bridge/status":{"get":{"tags":["Data › Bridge"],"summary":"Bridge transfer status","description":"Tracks a bridge transfer started via `/v1/actions/swap/x-chain` by polling the bridge's own tracking API. `status` is one of `PENDING`, `DONE`, `FAILED`, `TRANSFER_REFUNDED`, `INVALID`, `NOT_FOUND`, `PARTIAL_SUCCESS`. `NOT_FOUND` is normal in the first seconds after submission (indexer lag) — keep polling. `toHash` is the destination-chain transaction when the tracker exposes it. Responses are not cached.","parameters":[{"name":"bridge","in":"query","required":true,"schema":{"type":"string"},"description":"The `bridge` value from the executed quote","example":"Across"},{"name":"fromChainId","in":"query","required":true,"schema":{"type":"string"},"description":"Source chain ID of the transfer","example":"1"},{"name":"toChainId","in":"query","required":true,"schema":{"type":"string"},"description":"Destination chain ID","example":"8453"},{"name":"txHash","in":"query","schema":{"type":"string"},"description":"Source-chain transaction hash of the executed bridge transaction (required unless `orderId`)"},{"name":"tokenIn","in":"query","schema":{"type":"string"},"description":"Bridged input token address — REQUIRED for Stargate (pool-keyed tracker), ignored by most bridges"},{"name":"tokenOut","in":"query","schema":{"type":"string"},"description":"Bridged output token address — REQUIRED for Stargate"},{"name":"orderId","in":"query","schema":{"type":"string"},"description":"Intent order id for order-based bridges (Mayan)"}],"responses":{"200":{"description":"Transfer status","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","properties":{"bridge":{"type":"string"},"status":{"type":"string","enum":["PENDING","DONE","FAILED","TRANSFER_REFUNDED","INVALID","NOT_FOUND","PARTIAL_SUCCESS"]},"message":{"type":"string"},"fromHash":{"type":"string"},"toHash":{"type":"string","description":"Destination-chain tx hash when known"},"details":{"nullable":true,"description":"The tracker's raw response payload"}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/token/balances":{"get":{"tags":["Data › Token"],"summary":"Token balances","description":"Fetches token balances for a given account on a specific chain, including USD values.\n\n**Features:**\n- Automatically includes native token balance (ETH, BNB, etc.)\n- Fetches current prices for USD conversion\n- Returns both raw and formatted balance values\n\n**Response Structure:**\n- `items`: Array of balance entries for each token\n- `count`: Total number of balance entries\n\n**Balance Entry includes:**\n- `address`: Token contract address (zeroAddress for native)\n- `symbol`: Token symbol\n- `name`: Token name\n- `decimals`: Token decimals\n- `balanceRaw`: Raw balance as string (wei/smallest unit)\n- `balance`: Formatted balance as decimal string\n- `balanceUSD`: Balance value in USD\n\n**Note:** The native token balance is always included with address `0x0000000000000000000000000000000000000000`.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID to fetch balances on","example":"1"},{"name":"account","in":"query","required":true,"schema":{"type":"string"},"description":"EVM account address (0x-prefixed, 40 hex chars)","example":"0xbadA9c382165b31419F4CC0eDf0Fa84f80A3C8E5"},{"name":"assets","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated token addresses to fetch balances for","example":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48,0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"}],"responses":{"200":{"description":"Token balances with USD values","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/TokenBalancesResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/token/balances/rpc-call":{"get":{"tags":["Data › Token"],"summary":"Token balance RPC calls","description":"Prepares raw JSON-RPC `eth_call`s for fetching token balances using a batched balance-fetcher contract. Returns an `rpcCallId` and an array of `rpcCalls` that the integrator executes against their own RPC provider(s). The raw responses are then submitted to `/token/balances/parse` together with the `rpcCallId`. Two modes: **single-chain** (`chainId` + required `assets`; `rpcCalls` is a bare call array) and **multi-chain** (`chains` CSV, max 30; `rpcCalls` entries are `{ chainId, call }`). In multi-chain mode `assets` is optional — it defaults to each chain's curated `mainTokens` from the token lists, keeping balance scans small even where full lists hold tens of thousands of tokens (max 200 assets per chain). Chains without any known main tokens are reported in `skippedChains`.","parameters":[{"name":"chainId","in":"query","schema":{"type":"string"},"description":"Chain ID to fetch balances on (single-chain mode; required unless `chains` is given)","example":"1"},{"name":"chains","in":"query","schema":{"type":"string"},"description":"Comma-separated chain IDs (multi-chain mode, max 30). Takes precedence over `chainId`.","example":"1,10,8453"},{"name":"account","in":"query","required":true,"schema":{"type":"string"},"description":"EVM account address (0x-prefixed, 40 hex chars)","example":"0xbadA9c382165b31419F4CC0eDf0Fa84f80A3C8E5"},{"name":"assets","in":"query","schema":{"type":"string"},"description":"Comma-separated token addresses. Required in single-chain mode; optional in multi-chain mode (defaults to each chain’s `mainTokens`; when given, applied to every chain).","example":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48,0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"},{"name":"blockTag","in":"query","schema":{"type":"string","default":"latest"},"description":"Block tag for the RPC call","example":"latest"}],"responses":{"200":{"description":"Prepared RPC calls with a context ID for parsing","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RpcCallResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/token/balances/parse":{"post":{"tags":["Data › Token"],"summary":"Parse token balances","description":"Accepts the raw hex RPC response obtained by executing the call from `/token/balances/rpc-call` and decodes it into structured token balance data with USD values.\n\n**Workflow:**\n1. Call `/token/balances/rpc-call` to get prepared RPC calls and a `rpcCallId`\n2. Execute the RPC call against your own node\n3. Send `rpcCallId` + `rawResponses` to this endpoint for parsing\n\nThe `rpcCallId` ties the response back to the cached preparation context (valid for 5 minutes).\nAfter successful parsing the cached context is deleted.\n\n**Multi-chain mode** (when the rpc-call was made with `chains`): `rawResponses` entries are\n`{ chainId, result }` (result = hex string or full JSON-RPC envelope), and the response is\n`{ account, chains: [{ chainId, count, items }], missingChains? }` — chains whose response was\nnot submitted appear in `missingChains`.\n\n**Response Structure:**\n- `items`: Array of balance entries for each token\n- `count`: Total number of balance entries\n\n**Balance Entry includes:**\n- `address`: Token contract address (zeroAddress for native)\n- `symbol`: Token symbol\n- `name`: Token name\n- `decimals`: Token decimals\n- `balanceRaw`: Raw balance as string (wei/smallest unit)\n- `balance`: Formatted balance as decimal string\n- `balanceUSD`: Balance value in USD","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseTokenBalancesRequest"}}}},"responses":{"200":{"description":"Token balances with USD values","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/TokenBalancesResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"RPC call context not found or expired (older than 5 minutes). Call `/token/balances/rpc-call` again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/token/available":{"get":{"tags":["Data › Token"],"summary":"Get available lending assets","description":"Returns the list of assets available for lending on a given chain. Proxied from origin with a long-lived cache (1 hour).\n\n**Filters:**\n- `chainId` / `chainIds` — filter by chain\n- `lender` — filter by lender protocol\n- `symbol`, `name`, `address` — filter by asset metadata\n- `assetGroup` — filter by asset group\n\n**Response Structure:**\n- `count`: Number of matching assets\n- `items`: Array of asset objects with address, chain_id, symbol, name, and additional metadata","parameters":[{"name":"chainId","in":"query","schema":{"type":"string"},"description":"Filter by chain ID","example":"1"},{"name":"chainIds","in":"query","schema":{"type":"string"},"description":"Comma-separated chain IDs to filter by (alternative to chainId)","example":"1,42161"},{"name":"lender","in":"query","schema":{"type":"string"},"description":"Filter by lender protocol key","example":"AAVE_V3"},{"name":"symbol","in":"query","schema":{"type":"string"},"description":"Filter by token symbol","example":"USDC"},{"name":"name","in":"query","schema":{"type":"string"},"description":"Filter by token name (partial match)","example":"USD Coin"},{"name":"address","in":"query","schema":{"type":"string"},"description":"Filter by token contract address","example":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"},{"name":"assetGroup","in":"query","schema":{"type":"string"},"description":"Filter by asset group","example":"USDC"}],"responses":{"200":{"description":"Available lending assets","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/TokenAvailableResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/token/balances/lending":{"get":{"tags":["Data › Token"],"summary":"Lending token balances","description":"Fetches token balances for all available lending assets on a chain for a given account. Combines the available-assets lookup with balance fetching in a single call.\n\n**How it works:**\n1. Fetches all available lending asset addresses from origin (optionally filtered by lender)\n2. Fetches token balances for those assets via multicall\n3. Enriches with token metadata, prices, and USD values\n\n**Response Structure:**\n- `items`: Array of non-zero balance entries (same format as `/token/balances`)\n- `count`: Number of tokens with non-zero balance\n\n**Balance Entry includes:**\n- `address`: Token contract address (zeroAddress for native)\n- `symbol`: Token symbol\n- `name`: Token name\n- `decimals`: Token decimals\n- `balanceRaw`: Raw balance as string (wei/smallest unit)\n- `balance`: Formatted balance as decimal string\n- `priceUSD`: Per-unit USD price\n- `balanceUSD`: Balance value in USD\n\n**Note:** Zero-balance entries are excluded. The native token balance is included only if non-zero.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID to fetch balances on","example":"1"},{"name":"account","in":"query","required":true,"schema":{"type":"string"},"description":"EVM account address (0x-prefixed, 40 hex chars)","example":"0xbadA9c382165b31419F4CC0eDf0Fa84f80A3C8E5"},{"name":"lender","in":"query","schema":{"type":"string"},"description":"Optional lender filter — only fetch assets available on this protocol","example":"AAVE_V3"}],"responses":{"200":{"description":"Token balances for all lending assets with USD values","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/TokenBalancesResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/token/prices":{"get":{"tags":["Data › Token"],"summary":"Token prices by address","description":"Returns per-address USD prices for the given asset addresses on a chain.\n\n**How it works:**\n1. Resolves each address to its asset group via the token list\n2. Looks up the latest USD price for each asset group\n3. Returns a map of address → price\n\n**Query format:**\n- `assets`: Comma-separated list of EVM addresses (0x-prefixed, 40 hex chars)\n\n**Response Structure:**\n- `items`: Object mapping lowercase addresses to their USD price (0 if unknown)\n- `count`: Number of resolved assets","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"assets","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated list of token addresses","example":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"}],"responses":{"200":{"description":"Per-address USD prices","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/TokenPricesResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/vaults":{"get":{"tags":["Data › Vaults"],"summary":"Get vault data across providers","operationId":"vaults","description":"Returns public vault data across the supported ERC-4626-style providers on a single chain. By default this is served from the recorder origin — a DB-backed, USD-priced, **paginated** listing — and the response is a flat `{ start, count, items[] }` envelope (one entry per vault, see *Response shape* below).\n\n**Pagination** — `start` is the row offset and `limit` the page size; the origin returns up to one page per call. To walk a chain, request `start=0`, then `start=limit`, `start=2*limit`, … until fewer than `limit` items come back. This is intentional: large chains return hundreds of vaults and are paged rather than returned in one payload.\n\n**Legacy live mode** — pass `source=live` to bypass the origin and compute the data live on-chain via multicall. This returns the older **provider-keyed** `VaultPublicDataAll` shape (one key per provider, *not* `items[]`) and ignores `start`/`limit`. It is also the automatic fallback when no origin is configured (e.g. local dev) or the origin is unreachable.\n\n**Supported providers**\n\n| key | source |\n|---|---|\n| `fluid` | Fluid `fTokens` (ERC-4626 yield tokens) and vaults (NFT-position margin markets) |\n| `gearbox` | Gearbox V3 passive PoolV3 (ERC-4626 Diesel shares) |\n| `morpho` | Morpho Blue MetaMorpho vaults |\n| `lista` | Lista DAO earn vaults (Moolah-fork of MetaMorpho, BNB chain; fetched on-chain) |\n| `silo` | Silo V2 + V3 isolated lending vaults (GraphQL-backed) |\n| `euler-earn` | Euler V2 Earn vaults (ERC-4626) |\n| `lst` | Protocol-issued liquid-staking share tokens (Lido wstETH, Rocket Pool rETH, EtherFi weETH, Renzo ezETH, Kelp rsETH, Swell rswETH/swETH, Puffer pufETH, YieldNest ynETH, StakeWise osETH, Stader ETHx, Mantle mETH, Coinbase cbETH). Ethereum only in this drop. |\n| `savings` | ERC-4626 yield-bearing-stablecoin wrappers (Ethena sUSDe, Sky sUSDS/stUSDS, Maker sDAI, Reservoir wsrUSD, YieldFi yUSD, Resolv wstUSR, Angle stUSD/stEUR, Falcon sUSDf, InfiniFi siUSD, Maple syrupUSDC/syrupUSDT). Plus Avant savUSD on Avalanche and YO yoETH on Base. |\n| `yearn` | Yearn V3 vaults (VaultV3 + TokenizedStrategy ERC-4626) on Ethereum, Polygon, Base, Arbitrum, Gnosis, Sonic, Katana. Discovered + priced via the yDaemon API. |\n| `pendle` | Pendle V2 Principal Tokens as fixed-rate earn products — one row per **live** market. Not a share token (USD size, no share price); entry and exit are swaps. |\n| `spectra` | Spectra V2 Principal Tokens, same instrument as `pendle` over a rate-adjusted Curve StableSwap-NG pool. Live on Ethereum, Base, Flare, Katana, Hemi, HyperEVM and BNB; the API routes on a network NAME, so a new Spectra chain needs a registry entry before it appears. |\n| `aave-earn` | Aave Earn (\"stable\") vaults — curator-run ERC-4626 wrappers over an Aave v3 supply position. Discovered + priced via the Aave public GraphQL API (`api.v3.aave.com`) by tracked curator/vault, enriched on-chain for `totalSupply` + share price. |\n\n**Response shape**\n\n*Default (origin) mode:* `{ start, count, items[] }` where each item is a normalized, USD-priced vault record: `{ chainId, provider, vaultAddress, underlying, symbol, name, displayName, curatorName, decimals, assetDecimals, dataTs, updatedAt, rates: { depositRate, rewardsRate, totalRate, fee }, tvl: { totalAssets, totalSupply, totalAssetsFormatted, totalAssetsUsd }, liquidity: { liquidity, liquidityFormatted, liquidityUsd }, underlyingInfo, vaultInfo, curatorEntity, providerMeta, … }`. `totalAssetsUsd` / `liquidityUsd` are computed by the recorder from its own price store (see margin-fetcher `DATABASE_INTEGRATION.md`).\n\nThree metadata bundles travel with every item (mirroring lending's `underlyingInfo` + `lenderInfo`):\n\n- **`underlyingInfo`** — the underlying token: `{ asset: { chainId, address, symbol, name, decimals, logoURI, assetGroup, currencyId, props }, prices: { priceUsd, priceTs } }`.\n- **`vaultInfo`** — the vault's *own* identity: `{ address, symbol, name, logoURI, assetGroup, yieldProfile, denomination }`. `logoURI` is resolved from the **share token's** token-list entry, falling back to the underlying asset's logo. `name` uses the branded share-token name for `lst`/`savings` (e.g. *etherFi weETH*) and the curated label elsewhere (e.g. *Steakhouse USDC*).\n- **`curatorEntity`** — the curator's `{ name, logoUri }` from the curator registry, or `null`.\n\n*Legacy live mode (`source=live`):* the top-level object is keyed by provider name. Each provider's value is a free-form payload from `@1delta/margin-fetcher`'s `getVaultPublicDataAll` — schemas diverge per provider (see the package's types for exact field shapes). Common fields across most providers: vault/share token address, underlying asset, supply rate, total assets / supply, and provider-specific metadata (e.g. Fluid `vaultId`, Silo `siloAddress`).\n\n**LST deposit metadata (`providerMeta` on `lst` items)**\n\n`lst` records carry the data needed to build a deposit without reading SDK source:\n\n- `isMintable` — `false` ⇒ no permissionless on-chain mint (e.g. cbETH); skip the action endpoint.\n- `mintContract` — the mint target.\n- `exchangeRate` / `convertToShares` — to compute expected output and a `minOut`.\n- `isRebasing` — output-balance semantics (stETH/eETH rebase; wstETH/weETH don't).\n- `mintInputAsset` — the **primary** pay asset (`native` or an ERC-20 address).\n- **`acceptedInputs[]`** — the full accept-set, each `{ asset: \"native\"|\"0x…\", symbol?, mode: \"direct\"|\"wrap\"|\"submit-wrap\", needs?: string[] }`. `asset` is what to pass as `payAsset`; `needs` lists the option query params that path requires. This is the machine-readable answer to \"what can I pay with\"; drive the deposit request off it. See `GET /v1/actions/vaults/lst` for the build flow.\n- **`delegation`** — present only when the deposit requires/allows picking a validator/group/node/pool: `{ required, kind, optionKey, default, source }`. **Absent ⇒ pooled** (no picker needed). When present and `source: \"endpoint\"`, fetch the selectable set from [/v1/data/vaults/validators](#operation/vaults-validators) and send the chosen `id` back to the deposit as the param named by `delegation.optionKey`. This is the flag that tells the UI whether a validator fetch is needed. Attached by the worker in both origin and `source=live` modes.\n\n**Exit fees (`withdrawFeeBps`)**\n\nBasis points (`10` = 0.10 %), on `gearbox` pools and on `savings` entries whose `withdrawalMode` is `fee-or-queued` (Native Credit Pool `wNLP`). Distinct from `rates.fee`, which is a **performance** fee skimmed from yield — `withdrawFeeBps` is a one-off charge on the way **out**.\n\nFor Native `wNLP` specifically, an exit has two legs and only one of them is priced by this field:\n\n- **Instant** (`instantRedeem`) — the fee is deducted from the underlying paid to the receiver, never collected as a separate transfer, so the caller neither funds nor approves it: `received = shares × exchangeRate × (1 − withdrawFeeBps/10_000)`. Capped by `liquidity`, which is a **gross** figure — what actually lands is `liquidity × (1 − withdrawFeeBps/10_000)`.\n- **Queued** (`requestWithdrawal` → wait `withdrawalCooldownSeconds` → `claim`) — pays at par, so `withdrawFeeBps` does not apply. Its cost is implicit: the payout is snapshotted at request time and yield accruing over the wait goes to the protocol. At a 3-day window that is worth a few bps against a 100 bps instant fee, which is why the action builders default to the queue and require `instant=true` to take the haircut.\n\nThere is no deposit or management fee on these vaults — `exchangeRate` and the reported rates are already net. `instantRedeemEnabled: false` means the pool is queue-only and `withdrawFeeBps` is unreachable.\n\n**Lockup likelihood (`instantLiquidityRatio`, `savings` items)**\n\n`liquidity / totalAssets` clamped to `0…1` — the share of the vault exitable **this block**, so `1 − instantLiquidityRatio` is the share that must queue. `instant` vaults report `1`, cooldown/queued/request-based report `0`, and Native `fee-or-queued` pools report live coverage (observed across the full range: `1.00` on Ethereum `wNLP-USDC`, `0.20` on `wNLP-WBTC`, `0.0005` on BNB `wNLP-T4B`).\n\nNot named `utilization` on purpose: these protocols expose no debt accumulator, so `borrowed / supplied` is not readable per pool — this measures exit **coverage** instead, which is what actually predicts lockup. Two caveats: Native's buffer also custodies market-maker collateral so it can exceed the pool it serves (hence the clamp — `1` means \"fully covered\", not \"zero utilization\"), and it is a live spot reading that moves as market makers draw inventory. Nothing is lost below `1`; the remainder redeems at par through the queue.\n\n**Related action endpoints**\n\n- `GET /v1/actions/vaults/deposit` — ERC-4626 deposit via the 1delta Composer, with optional Fluid native-deposit path. Also serves Native `wNLP` (auto-detected; no `underlying` needed).\n- `GET /v1/actions/vaults/withdraw` — ERC-4626 withdraw via the 1delta Composer, with native-unwrap support. Also serves Native `wNLP` (`instant=true` for the fee-paying leg).\n- `GET /v1/actions/vaults/lst` — LST/LRT mint · withdraw-request · claim · cancel, driven by the `acceptedInputs` above.\n- `GET /v1/actions/vaults/savings` — cooldown/queued savings exits, plus Native `wNLP` deposit · request-withdraw · claim · cancel.\n\nFor Fluid **margin vaults** (the NFT-position kind, not fTokens), the lending action endpoints (`/v1/actions/lending/{deposit,withdraw,borrow,repay}`) are the right surface — they understand the per-vault `FLUID_<chainId>_<vaultId>` lender keys and Fluid's NFT ownership model (including the pre-flight `ownerOf` validation for deposit-to-existing-NFT with a custom `receiver`).","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID to query","example":"1"},{"name":"start","in":"query","schema":{"type":"integer"},"description":"Pagination offset (origin mode). Row index of the first item to return. Non-negative integer; defaults to 0.","example":0},{"name":"limit","in":"query","schema":{"type":"integer"},"description":"Pagination page size (origin mode). Max items to return per call. Non-negative integer. Ignored in `source=live` mode.","example":100},{"name":"provider","in":"query","schema":{"type":"string"},"description":"Origin mode: narrow the listing to a single provider (e.g. `morpho`).","example":"morpho"},{"name":"source","in":"query","schema":{"type":"string","enum":["live"]},"description":"Set to `live` to bypass the origin and compute on-chain via multicall (returns the legacy provider-keyed shape; `start`/`limit` ignored)."},{"name":"providers","in":"query","schema":{"type":"string"},"description":"Legacy live mode only (`source=live`): CSV of vault providers to include. Defaults to all (`fluid,gearbox,morpho,lista,silo,euler-earn,lst,savings,lagoon,aave-earn,upshift,yearn,hypercore,gmx`). Unknown providers return 400.","example":"fluid,morpho"},{"name":"includeExpired","in":"query","schema":{"type":"string","enum":["true","false"],"default":"false"},"description":"Legacy live mode only (`source=live`): include MATURED fixed-maturity markets (`pendle`, `spectra`). Off by default and deliberately so — a matured principal token keeps publishing its last pre-expiry implied APY, which on a rate-sorted list is a rate that no longer exists on a product that no longer exists. Set it only for a holder-facing redeem flow. Note Spectra's upstream listing appears to drop matured markets itself, so this may return nothing for that provider."},{"name":"siloProtocolVersion","in":"query","schema":{"type":"string","enum":["v2","v3"]},"description":"Narrow the Silo query to a single protocol version. Has no effect on other providers."},{"name":"siloLimit","in":"query","schema":{"type":"integer"},"description":"Page-size hint for the Silo GraphQL query (positive integer)."},{"name":"terms","in":"query","schema":{"type":"string","enum":["digest","full","none"],"default":"digest"},"description":"Term-sheet depth attached to every vault as `termSheet` — the SAME shape and the same parameter as `/v1/data/lending/latest`, so one renderer handles an Aave reserve and a Pendle PT. A vault carries `termSheet.supply` only; `borrow` is absent, and that absence is the statement that it cannot be borrowed. `digest` (default) is the compact form; `full` inlines `info.description`, `backedBy.items[]` and the `coverage` map; `none` omits the field. **Read `coverage` before trusting a silence** — it separates \"does not apply here\" (`notApplicable`) from \"not wired yet\" (`pending`), so an absent `governance` block never reads as \"ungoverned\". Origin mode only.","example":"full"}],"responses":{"200":{"description":"Origin mode: `{ start, count, items[] }` paginated, USD-priced vault records. Legacy live mode (`source=live`): provider-keyed vault data, with failed providers omitted (the worker logs a warning but does not propagate per-provider errors).","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","additionalProperties":true},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/vaults/user":{"get":{"tags":["Data › Vaults"],"summary":"Get user balances across ERC-4626 vaults","operationId":"vaults-user","description":"Returns per-vault balances for `account` across the supplied ERC-4626 share-token addresses. Static vault metadata (underlying, decimals, share-price ratio) is sourced from the cached `/v1/data/vaults` public-data fetch, so this endpoint's only on-chain footprint is exactly one `balanceOf(account)` per vault.\n\n**Why this exists**\n\nVault shares are plain ERC-20s, so `/v1/data/token/balances` can read them — but it can't convert shares → assets. This endpoint folds that conversion in so the frontend doesn't have to redo `shares * totalAssets / totalSupply` math (and get the rounding subtly wrong).\n\n**Scope**\n\nVault addresses must be tracked by `/v1/data/vaults` (the 5 supported providers — `fluid`, `gearbox`, `morpho`, `silo`, `euler-earn`). Unknown addresses are listed back to the caller in the response's `unknown` field; a request that contains *only* unknown addresses returns 400. Untracked vaults are intentionally not auto-included — the 1delta data layer only surfaces vaults that have passed risk review.\n\n**Per-item fields** (naming mirrors `/v1/data/token/balances` — `<thing>Raw` is a stringified uint, `<thing>` is the formatted human-readable string)\n\n| field | meaning |\n|---|---|\n| `vault` | share-token address (echoes the input) |\n| `underlying` | result of `vault.asset()` |\n| `symbol`, `name`, `decimals` | from the token registry / on-chain `decimals()` |\n| `sharesRaw` / `shares` | raw uint + formatted balance in vault tokens |\n| `assetsRaw` / `assets` | shares converted to underlying at the current fair share price (via `convertToAssets`) — raw uint + formatted |\n| `priceUSD` | unit price of the underlying |\n| `balanceUSD` | `assets * priceUSD` — USD value of the position |\n\n**Resilience**\n\nFailed reads (e.g. a non-ERC-4626 address slipped into the list, or a vault that reverts on `convertToAssets`) are silently dropped from the response — the whole request is not failed. Vaults the user hasn't deposited into (zero shares) are kept in the response so the caller can render a deterministic table.\n\n**Scope**\n\nThis is for passive ERC-4626 vaults (Fluid fTokens, MetaMorpho, Gearbox V3 pools, Silo, Euler Earn). For Fluid's NFT-position **margin vaults**, use `/v1/data/lending/user-positions` instead — it understands the `FLUID_<chainId>_<vaultId>` lender keys and the NFT-per-position model.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"account","in":"query","required":true,"schema":{"type":"string"},"description":"User address (EOA or contract)","example":"0xYourAddress"},{"name":"vaults","in":"query","required":true,"schema":{"type":"string"},"description":"CSV of ERC-4626 share-token addresses to query. Duplicates are de-duped; invalid addresses are skipped.","example":"0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8,0xBEEF01735c132Ada46AA9aA4c54623caA92A64CB"}],"responses":{"200":{"description":"Per-vault balances joined with underlying prices. Items with failed reads are omitted; zero-share entries are kept.","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","additionalProperties":true},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/vaults/validators":{"get":{"tags":["Data › Vaults"],"summary":"Get selectable validators for an LST deposit","operationId":"vaults-validators","description":"Returns the selectable **delegation targets** (validators / validator-groups / nodes) for an LST that lets — or requires — the depositor to choose where the stake is delegated. The companion to the `delegation` descriptor carried on each `lst` item in [/v1/data/vaults](#operation/vaults): the descriptor says *whether* a choice is needed and the deposit option key to send it back as; this endpoint returns the live set.\n\n**The flow**\n\n1. `/v1/data/vaults?provider=lst` → an item's `providerMeta.delegation` tells you if a choice is needed:\n   - **absent** ⇒ pooled LST (Lido, Rocket Pool, …) — no picker, deposit directly.\n   - `{ required, kind, optionKey, default, source }` ⇒ a selection applies.\n2. If `source: \"endpoint\"`, call this endpoint to populate the picker.\n3. Send the chosen item's `id` back to [/v1/actions/vaults/deposit](#operation/vaultsDeposit) as the param named by `delegation.optionKey` (e.g. `validator=<id>` for Core, `validatorGroup=<id>` for Celo).\n\n**Behaviour by LST type**\n\n- **Required** (Core stCORE — `validator`): the picker is mandatory; preselect the `recommended` item.\n- **Optional / auto** (Celo stCELO — `validatorGroup`, `default: \"auto\"`): the picker is optional — omit the choice and the deposit endpoint auto-resolves a valid group. Offer it as an \"advanced\" control.\n- **Off-chain** (Solv — `poolId`, `source: \"offchain\"`): `items[]` is empty — the value comes from the protocol's docs, not the chain. Render an input, not a list.\n- **Pooled / unknown**: `delegation: null`, empty `items[]` (200, not 404) so a UI can treat \"no selection\" uniformly.\n\n**Per-item fields**\n\n| field | meaning |\n|---|---|\n| `id` | the opaque value to pass back as `delegation.optionKey` (validator / group / node address) |\n| `status` | `active` · `inactive` · `jailed` · `full` — only `active` targets are `selectable` |\n| `selectable` | passes the protocol's eligibility (healthy + not blocked + has room) |\n| `recommended` | the default pick (best capacity / health) — preselect this |\n| `receivableVotes` | remaining capacity (raw underlying string), where the protocol exposes it (Celo) — show as \"room left\" so a user avoids a near-full target |\n\nOnly the selectable set is returned today; per-validator APR / commission are populated as clean sources are wired (absent ⇒ not yet available, never approximated).\n\nResults are cached ~60s (validator metadata moves per epoch/round, not per block).","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"42220"},{"name":"shareToken","in":"query","required":true,"schema":{"type":"string"},"description":"LST share-token address (alias: `vault`).","example":"0xc668583dcbdc9ae6fa3ce46462758188adfdfc24"}],"responses":{"200":{"description":"`{ chainId, shareToken, delegation, start, count, items[] }`. `delegation` is null for pooled/unknown share tokens; `items[]` is empty for pooled or off-chain (Solv poolId) selections.","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","additionalProperties":true},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/data/vaults/withdrawals":{"get":{"tags":["Data › Vaults"],"summary":"Get a user's pending vault withdrawal requests","operationId":"vaults-withdrawals","description":"Returns a user's **pending withdrawal requests** across every vault that has a withdrawal queue / cooldown (LSTs, savings cooldowns, ERC-7540 async vaults). The static LST/savings withdrawal registry is always probed; pass `vaults` to additionally read arbitrary ERC-7540 vaults by address.\n\nEach entry is normalised to a uniform shape so the frontend treats every protocol identically — render the list, and when `status === \"claimable\"` build the claim via the matching action endpoint ([/v1/actions/vaults/lst](#operation/vaultsLst) or [/savings](#operation/vaultsSavings)).\n\n**Response:** `{ chainId, account, count, requests }`, where each `requests[]` entry carries:\n\n| field | meaning |\n|---|---|\n| `lst` | the share-token the request was made against |\n| `brand`, `symbol` | UI labels |\n| `requestId` | protocol-native id (NFT tokenId, queue index, 7540 requestId, or a bucket name like `waiting`/`unbonded`) — pass back to the claim action |\n| `amountUnderlying` | wei the request returns on claim (expected amount for floating-rate queues) |\n| `status` | `pending` · `claimable` · `claimed` · `expired` |\n| `readyAt` | unix seconds the request becomes claimable (fixed-cooldown protocols only) |\n| `yieldProfile`, `denomination` | present when the vault is in the public-data lookup |\n\nEmpty per-vault results are dropped.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"account","in":"query","required":true,"schema":{"type":"string"},"description":"0x-prefixed holder address.","example":"0xYourAddress"},{"name":"vaults","in":"query","schema":{"type":"string"},"description":"Optional CSV of extra ERC-7540 vault addresses to probe with the generic async-redeem reader, in addition to the static registry."}],"responses":{"200":{"description":"Flat `requests[]` of pending withdrawal requests across all queued/cooldown vaults for `account`.","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","additionalProperties":true},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions":{"get":{"tags":["Actions › Index"],"summary":"List action endpoints","description":"Returns a flat directory of every action endpoint, grouped by category (`lending`, `loop`, `swap`, `allocate`). Useful for client-side discovery and link generation.","responses":{"200":{"description":"Endpoint listing","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","additionalProperties":true},"actions":{"type":"null"}}}}}}}}},"/v1/actions/lending/deposit":{"get":{"tags":["Actions › Lending"],"summary":"Deposit","operationId":"lending-deposit","description":"Build calldata for depositing into a lending pool. Use `mode=direct` (default) for raw protocol interaction or `mode=proxy` for 1delta composer. Identify the market via `marketUid` (format: `lender:chainId:address`). Approval transactions are automatically filtered: if the user already has sufficient allowances, `permissions` (envelope) and `permissionTxns` (per-entry) will be empty. Pass `simulate=true` to include projected post-trade metrics.\n\n**Morpho Midnight order-book markets (`MORPHO_MIDNIGHT_<id>`):** depositing = **lending** = **TAKING** the ask side of the book — this endpoint fills the `lendOffers` returned by [`/v1/data/lending/latest?includeOffers=true`](#operation/lending-latest), best-first. To post your own limit offer instead (MAKE), use [`/v1/actions/midnight/make`](#operation/midnight-make).","parameters":[{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier (`lender:chainId:address`). The address encodes the underlying token (Aave/Morpho/CompoundV3), cToken (CompoundV2), or pool (Init Capital).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"mode","in":"query","schema":{"type":"string","enum":["proxy","direct"],"default":"direct"},"description":"Execution mode. proxy = 1delta composer, direct = raw protocol"},{"name":"operator","in":"query","schema":{"type":"string"},"description":"Wallet address of the user executing the action","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient of the lender position (deposit) or underlying tokens (borrow/withdraw).\nDefaults to `operator` when omitted.\n\nCustom-receiver support is per-lender. The API auto-selects the execution path:\n\n**Direct path supported**\n\n- Aave V2/V3 — `supply` / `withdraw` honor `to`.\n- Aave V4 — deposit via GiverPM `supplyOnBehalfOf`. Requires the GiverPM to be curated in `aave-v4-peripherals.json` with a name containing \"giver\".\n- Compound V3 — `supplyTo` / `withdrawTo`.\n- Morpho Blue & Lista DAO ERC-20 markets — Morpho `onBehalf`.\n- Euler V2 ERC-20 vaults — ERC-4626 `deposit(receiver)`. Collateral auto-enable is dropped for non-sub-account receivers.\n- Silo V2/V3 ERC-20 — `deposit(_, receiver, _)`.\n- Gearbox V3 passive-pool deposits — ERC-4626 `deposit(_, receiver)`.\n- Fluid borrow & withdraw — `operate(..., to_)` outflow slot.\n- Fluid **deposit to an existing NFT** when `accountId` is supplied — pure-deposit `operate()` calls bypass Fluid's owner auth gate, and the API folds a pre-flight `VaultFactory.ownerOf(nftId) == receiver` check into the merged multicall (rejects with 400 `INVALID_PARAM` on mismatch, `VALIDATION_FAILED` if the RPC can't confirm ownership).\n\n**Routes to composer (proxy) path**\n\n- Compound V2 family — direct `mint` is `msg.sender`-only.\n- Fluid **fresh-open deposits** — no `accountId` or `accountId=0`. The composer mints the new position NFT to itself, then transfers it to `receiver` via the encoder's `nftReceiver` slot.\n- Native-ETH vaults on Fluid (deposit / withdraw / repay) and the CompoundV2 family — the composer forwards `msg.value` to the vault's payable entrypoint. Other lenders' composer paths reject native asset and require wrapped-native as `payAsset`.\n\n**Direct-only branches that throw**\n\n- Gearbox V3 credit-side `addCollateral` — CA bound to operator.\n- Aave V4 native-gateway path — `supplyAsCollateralNative` has no recipient slot.\n\n**Unsupported in any single tx**\n\n- Init Capital — position NFT swept to operator.\n- Euler V2 / Silo V2/V3 native deposit paths — router/orchestrator credits msg.sender.\n- Fluid repay — on-chain primitive permits it, direct handler doesn't expose a receiver slot yet (routes to proxy as a conservative default).\n\nWhen the lender cannot honor a custom receiver on the direct path, `getTarget` falls back to `proxy` automatically. Forcing `mode=direct` on an unsupported path either throws (Gearbox credit-side, Aave V4 spoke, the explicit Silo router check) or silently credits the operator instead (Compound V2, Init, and Fluid fresh-open)."},{"name":"payAsset","in":"query","schema":{"type":"string"},"description":"Asset to pay with.\n\nUse the zero address to pay with native ETH on lenders whose vault accepts native (e.g. Init Capital, Fluid native-ETH vaults, CompoundV2 `cETH` markets).\n\nProxy mode: defaults to market asset. Native is forwarded as `msg.value` only when the lender supports it (e.g. Fluid, CompoundV2); otherwise the composer rejects."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"Amount is in shares (direct mode)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Per-position identifier. Lender-specific:\n\n- **Init Capital** — account ID, required for borrow/withdraw/repay.\n- **Euler V2** — sub-account index (0..255), defaults to 0.\n- **Gearbox V3** — credit-account address (alias: `creditAccount`).\n- **Fluid** — position NFT id. Omit or pass `0` to mint a new position; supply an existing nftId to act on an existing position. Required for borrow/withdraw/repay; for deposit it unlocks the direct path with a custom `receiver` (deposit lands on the receiver-owned NFT after pre-flight `ownerOf` validation)."},{"name":"slippage","in":"query","schema":{"type":"number"},"description":"Slippage tolerance in **basis points** (`50` = 0.5%, `100` = 1%).\n\nRequired whenever the action has to price something:\n\n- **Aggregator swap** — when `payAsset` / `receiveAsset` differs from the\n  market's underlying, the currency conversion is routed through a DEX\n  aggregator and this bounds that swap.\n- **Order-book take (Morpho Midnight)** — bounds the worst execution price\n  accepted while filling offers.\n\nIgnored when neither applies (same-asset action on a pool lender).","example":50},{"name":"simulate","in":"query","schema":{"type":"boolean"},"description":"When true, the API fetches the user's on-chain balances and returns projected post-trade metrics in the `simulation` field. Default: false."}],"responses":{"200":{"description":"Transaction calldata and approvals for deposit","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/LendingRawResponse"},{"$ref":"#/components/schemas/LendingProxyResponse"}],"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Actions › Lending"],"summary":"Deposit (simulate)","description":"Build calldata for depositing into a lending pool and simulate post-trade state. Same parameters as GET. Optionally send a JSON body with current portfolio state (`balanceData`, `aprData`, `positions`) to receive projected post-trade metrics in the `simulation` field — if omitted, the API fetches balances on-chain automatically. Use the data returned by the user-positions endpoint directly — always include `positions` for accurate health-factor and borrow-capacity projections.","parameters":[{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier (`lender:chainId:address`). The address encodes the underlying token (Aave/Morpho/CompoundV3), cToken (CompoundV2), or pool (Init Capital).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"mode","in":"query","schema":{"type":"string","enum":["proxy","direct"],"default":"direct"},"description":"Execution mode. proxy = 1delta composer, direct = raw protocol"},{"name":"operator","in":"query","schema":{"type":"string"},"description":"Wallet address of the user executing the action","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient of the lender position (deposit) or underlying tokens (borrow/withdraw).\nDefaults to `operator` when omitted.\n\nCustom-receiver support is per-lender. The API auto-selects the execution path:\n\n**Direct path supported**\n\n- Aave V2/V3 — `supply` / `withdraw` honor `to`.\n- Aave V4 — deposit via GiverPM `supplyOnBehalfOf`. Requires the GiverPM to be curated in `aave-v4-peripherals.json` with a name containing \"giver\".\n- Compound V3 — `supplyTo` / `withdrawTo`.\n- Morpho Blue & Lista DAO ERC-20 markets — Morpho `onBehalf`.\n- Euler V2 ERC-20 vaults — ERC-4626 `deposit(receiver)`. Collateral auto-enable is dropped for non-sub-account receivers.\n- Silo V2/V3 ERC-20 — `deposit(_, receiver, _)`.\n- Gearbox V3 passive-pool deposits — ERC-4626 `deposit(_, receiver)`.\n- Fluid borrow & withdraw — `operate(..., to_)` outflow slot.\n- Fluid **deposit to an existing NFT** when `accountId` is supplied — pure-deposit `operate()` calls bypass Fluid's owner auth gate, and the API folds a pre-flight `VaultFactory.ownerOf(nftId) == receiver` check into the merged multicall (rejects with 400 `INVALID_PARAM` on mismatch, `VALIDATION_FAILED` if the RPC can't confirm ownership).\n\n**Routes to composer (proxy) path**\n\n- Compound V2 family — direct `mint` is `msg.sender`-only.\n- Fluid **fresh-open deposits** — no `accountId` or `accountId=0`. The composer mints the new position NFT to itself, then transfers it to `receiver` via the encoder's `nftReceiver` slot.\n- Native-ETH vaults on Fluid (deposit / withdraw / repay) and the CompoundV2 family — the composer forwards `msg.value` to the vault's payable entrypoint. Other lenders' composer paths reject native asset and require wrapped-native as `payAsset`.\n\n**Direct-only branches that throw**\n\n- Gearbox V3 credit-side `addCollateral` — CA bound to operator.\n- Aave V4 native-gateway path — `supplyAsCollateralNative` has no recipient slot.\n\n**Unsupported in any single tx**\n\n- Init Capital — position NFT swept to operator.\n- Euler V2 / Silo V2/V3 native deposit paths — router/orchestrator credits msg.sender.\n- Fluid repay — on-chain primitive permits it, direct handler doesn't expose a receiver slot yet (routes to proxy as a conservative default).\n\nWhen the lender cannot honor a custom receiver on the direct path, `getTarget` falls back to `proxy` automatically. Forcing `mode=direct` on an unsupported path either throws (Gearbox credit-side, Aave V4 spoke, the explicit Silo router check) or silently credits the operator instead (Compound V2, Init, and Fluid fresh-open)."},{"name":"payAsset","in":"query","schema":{"type":"string"},"description":"Asset to pay with.\n\nUse the zero address to pay with native ETH on lenders whose vault accepts native (e.g. Init Capital, Fluid native-ETH vaults, CompoundV2 `cETH` markets).\n\nProxy mode: defaults to market asset. Native is forwarded as `msg.value` only when the lender supports it (e.g. Fluid, CompoundV2); otherwise the composer rejects."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"Amount is in shares (direct mode)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Per-position identifier. Lender-specific:\n\n- **Init Capital** — account ID, required for borrow/withdraw/repay.\n- **Euler V2** — sub-account index (0..255), defaults to 0.\n- **Gearbox V3** — credit-account address (alias: `creditAccount`).\n- **Fluid** — position NFT id. Omit or pass `0` to mint a new position; supply an existing nftId to act on an existing position. Required for borrow/withdraw/repay; for deposit it unlocks the direct path with a custom `receiver` (deposit lands on the receiver-owned NFT after pre-flight `ownerOf` validation)."},{"name":"slippage","in":"query","schema":{"type":"number"},"description":"Slippage tolerance in **basis points** (`50` = 0.5%, `100` = 1%).\n\nRequired whenever the action has to price something:\n\n- **Aggregator swap** — when `payAsset` / `receiveAsset` differs from the\n  market's underlying, the currency conversion is routed through a DEX\n  aggregator and this bounds that swap.\n- **Order-book take (Morpho Midnight)** — bounds the worst execution price\n  accepted while filling offers.\n\nIgnored when neither applies (same-asset action on a pool lender).","example":50},{"name":"simulate","in":"query","schema":{"type":"boolean"},"description":"When true, the API fetches the user's on-chain balances and returns projected post-trade metrics in the `simulation` field. Default: false."}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. If omitted, the API fetches balances on-chain automatically (slower, no `simulation` in response). When provided, pass `balanceData`, `aprData`, and `positions` directly from the matching sub-account in the `/v1/data/lending/user-positions` response — see the `SimulationBody` schema for a step-by-step example.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationBody"}}}},"responses":{"200":{"description":"Transaction calldata with post-trade simulation","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/LendingRawSimulationResponse"},{"$ref":"#/components/schemas/LendingProxySimulationResponse"}],"description":"Simulation results including projected health factor and borrow capacity"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/lending/simulate":{"get":{"tags":["Actions › Lending"],"summary":"Simulate a base lending operation on-chain","operationId":"lending-simulate","description":"Build a base lending operation and **execute it against live chain state** before the user signs it.\n\nThis is an EVM check, not the arithmetic projection that `simulate=true` adds to the other lending endpoints. The transaction is built by the very endpoint the client would call (`/v1/actions/lending/{action}`) and then replayed through an `eth_call` with storage overrides — so what is verified is the exact calldata that would be submitted, including the lender quirks that only surface on-chain.\n\n**Scope is the four single-grant operations.** `deposit` and `repay` override the pay token (funding balance and approval in one write); `withdraw` and `borrow` override the lender's own grant mapping (Aave credit delegation, Morpho `isAuthorized`, Comet `isAllowed`) so the action can be previewed *before* the user has signed that permission. Anything needing two grants at once — notably a leverage open that pulls fresh margin **and** borrows on behalf — cannot be previewed cold and is out of scope.\n\n**The override is optimistic.** `willSucceed: true` with `grantOverridden: true` means \"this succeeds once the permission exists\"; it is not evidence the permission exists. Keep gating the real grant on the permissions array.\n\nThe `simulation` object is either:\n- `{ simulated: true, willSucceed, gasUsed, amountOut, returnData, revertReason?, block, grantOverridden }`\n- `{ simulated: false, reason, detail }` — where `reason` is one of `NO_TRANSACTION`, `MULTI_STEP`, `NATIVE_VALUE`, `MULTIPLE_GRANTS`, `GRANT_SLOT_UNKNOWN`, `SIMULATOR_UNAVAILABLE`.\n\nNote `amountOut` is a measured balance delta on `operator`, so it reads `0` for lenders with no receipt token (Morpho Blue and every CDP-shaped lender) — those return their own values in `returnData` instead. A simulator outage never fails the request: the calldata is still returned and remains valid.","parameters":[{"name":"action","in":"query","required":true,"schema":{"type":"string"},"description":"Which base operation to build and replay: `deposit`, `withdraw`, `borrow` or `repay`."},{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier (`lender:chainId:address`). The address encodes the underlying token (Aave/Morpho/CompoundV3), cToken (CompoundV2), or pool (Init Capital).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"mode","in":"query","schema":{"type":"string","enum":["proxy","direct"],"default":"direct"},"description":"Execution mode. proxy = 1delta composer, direct = raw protocol"},{"name":"operator","in":"query","schema":{"type":"string"},"description":"Wallet address of the user executing the action","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient of the lender position (deposit) or underlying tokens (borrow/withdraw).\nDefaults to `operator` when omitted.\n\nCustom-receiver support is per-lender. The API auto-selects the execution path:\n\n**Direct path supported**\n\n- Aave V2/V3 — `supply` / `withdraw` honor `to`.\n- Aave V4 — deposit via GiverPM `supplyOnBehalfOf`. Requires the GiverPM to be curated in `aave-v4-peripherals.json` with a name containing \"giver\".\n- Compound V3 — `supplyTo` / `withdrawTo`.\n- Morpho Blue & Lista DAO ERC-20 markets — Morpho `onBehalf`.\n- Euler V2 ERC-20 vaults — ERC-4626 `deposit(receiver)`. Collateral auto-enable is dropped for non-sub-account receivers.\n- Silo V2/V3 ERC-20 — `deposit(_, receiver, _)`.\n- Gearbox V3 passive-pool deposits — ERC-4626 `deposit(_, receiver)`.\n- Fluid borrow & withdraw — `operate(..., to_)` outflow slot.\n- Fluid **deposit to an existing NFT** when `accountId` is supplied — pure-deposit `operate()` calls bypass Fluid's owner auth gate, and the API folds a pre-flight `VaultFactory.ownerOf(nftId) == receiver` check into the merged multicall (rejects with 400 `INVALID_PARAM` on mismatch, `VALIDATION_FAILED` if the RPC can't confirm ownership).\n\n**Routes to composer (proxy) path**\n\n- Compound V2 family — direct `mint` is `msg.sender`-only.\n- Fluid **fresh-open deposits** — no `accountId` or `accountId=0`. The composer mints the new position NFT to itself, then transfers it to `receiver` via the encoder's `nftReceiver` slot.\n- Native-ETH vaults on Fluid (deposit / withdraw / repay) and the CompoundV2 family — the composer forwards `msg.value` to the vault's payable entrypoint. Other lenders' composer paths reject native asset and require wrapped-native as `payAsset`.\n\n**Direct-only branches that throw**\n\n- Gearbox V3 credit-side `addCollateral` — CA bound to operator.\n- Aave V4 native-gateway path — `supplyAsCollateralNative` has no recipient slot.\n\n**Unsupported in any single tx**\n\n- Init Capital — position NFT swept to operator.\n- Euler V2 / Silo V2/V3 native deposit paths — router/orchestrator credits msg.sender.\n- Fluid repay — on-chain primitive permits it, direct handler doesn't expose a receiver slot yet (routes to proxy as a conservative default).\n\nWhen the lender cannot honor a custom receiver on the direct path, `getTarget` falls back to `proxy` automatically. Forcing `mode=direct` on an unsupported path either throws (Gearbox credit-side, Aave V4 spoke, the explicit Silo router check) or silently credits the operator instead (Compound V2, Init, and Fluid fresh-open)."},{"name":"payAsset","in":"query","schema":{"type":"string"},"description":"Asset to pay with.\n\nUse the zero address to pay with native ETH on lenders whose vault accepts native (e.g. Init Capital, Fluid native-ETH vaults, CompoundV2 `cETH` markets).\n\nProxy mode: defaults to market asset. Native is forwarded as `msg.value` only when the lender supports it (e.g. Fluid, CompoundV2); otherwise the composer rejects."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"Amount is in shares (direct mode)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Per-position identifier. Lender-specific:\n\n- **Init Capital** — account ID, required for borrow/withdraw/repay.\n- **Euler V2** — sub-account index (0..255), defaults to 0.\n- **Gearbox V3** — credit-account address (alias: `creditAccount`).\n- **Fluid** — position NFT id. Omit or pass `0` to mint a new position; supply an existing nftId to act on an existing position. Required for borrow/withdraw/repay; for deposit it unlocks the direct path with a custom `receiver` (deposit lands on the receiver-owned NFT after pre-flight `ownerOf` validation)."},{"name":"slippage","in":"query","schema":{"type":"number"},"description":"Slippage tolerance in **basis points** (`50` = 0.5%, `100` = 1%).\n\nRequired whenever the action has to price something:\n\n- **Aggregator swap** — when `payAsset` / `receiveAsset` differs from the\n  market's underlying, the currency conversion is routed through a DEX\n  aggregator and this bounds that swap.\n- **Order-book take (Morpho Midnight)** — bounds the worst execution price\n  accepted while filling offers.\n\nIgnored when neither applies (same-asset action on a pool lender).","example":50},{"name":"simulate","in":"query","schema":{"type":"boolean"},"description":"When true, the API fetches the user's on-chain balances and returns projected post-trade metrics in the `simulation` field. Default: false."},{"name":"block","in":"query","schema":{"type":"string"},"description":"Block number or tag to simulate against. Defaults to `latest`."}],"responses":{"200":{"description":"Transaction calldata plus the on-chain execution result"},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/lending/withdraw":{"get":{"tags":["Actions › Lending"],"summary":"Withdraw","operationId":"lending-withdraw","description":"Build calldata for withdrawing from a lending pool. Identify the market via `marketUid` (format: `lender:chainId:address`). Approval transactions are automatically filtered: if the user already has sufficient allowances, `permissions` (envelope) and `permissionTxns` (per-entry) will be empty. Pass `simulate=true` to include projected post-trade metrics. When on-chain deposit data is available and `amount` covers the full deposit balance, the API uses protocol-level max-withdraw mechanisms automatically (e.g. `maxUint256` or share-based redemption). If `isAll=true` but `amount` is less than the deposit balance, the API falls back to a partial withdrawal to avoid reverts.","parameters":[{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier (`lender:chainId:address`). The address encodes the underlying token (Aave/Morpho/CompoundV3), cToken (CompoundV2), or pool (Init Capital).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"mode","in":"query","schema":{"type":"string","enum":["proxy","direct"],"default":"direct"},"description":"Execution mode. proxy = 1delta composer, direct = raw protocol"},{"name":"operator","in":"query","schema":{"type":"string"},"description":"Wallet address of the user executing the action","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient of the lender position (deposit) or underlying tokens (borrow/withdraw).\nDefaults to `operator` when omitted.\n\nCustom-receiver support is per-lender. The API auto-selects the execution path:\n\n**Direct path supported**\n\n- Aave V2/V3 — `supply` / `withdraw` honor `to`.\n- Aave V4 — deposit via GiverPM `supplyOnBehalfOf`. Requires the GiverPM to be curated in `aave-v4-peripherals.json` with a name containing \"giver\".\n- Compound V3 — `supplyTo` / `withdrawTo`.\n- Morpho Blue & Lista DAO ERC-20 markets — Morpho `onBehalf`.\n- Euler V2 ERC-20 vaults — ERC-4626 `deposit(receiver)`. Collateral auto-enable is dropped for non-sub-account receivers.\n- Silo V2/V3 ERC-20 — `deposit(_, receiver, _)`.\n- Gearbox V3 passive-pool deposits — ERC-4626 `deposit(_, receiver)`.\n- Fluid borrow & withdraw — `operate(..., to_)` outflow slot.\n- Fluid **deposit to an existing NFT** when `accountId` is supplied — pure-deposit `operate()` calls bypass Fluid's owner auth gate, and the API folds a pre-flight `VaultFactory.ownerOf(nftId) == receiver` check into the merged multicall (rejects with 400 `INVALID_PARAM` on mismatch, `VALIDATION_FAILED` if the RPC can't confirm ownership).\n\n**Routes to composer (proxy) path**\n\n- Compound V2 family — direct `mint` is `msg.sender`-only.\n- Fluid **fresh-open deposits** — no `accountId` or `accountId=0`. The composer mints the new position NFT to itself, then transfers it to `receiver` via the encoder's `nftReceiver` slot.\n- Native-ETH vaults on Fluid (deposit / withdraw / repay) and the CompoundV2 family — the composer forwards `msg.value` to the vault's payable entrypoint. Other lenders' composer paths reject native asset and require wrapped-native as `payAsset`.\n\n**Direct-only branches that throw**\n\n- Gearbox V3 credit-side `addCollateral` — CA bound to operator.\n- Aave V4 native-gateway path — `supplyAsCollateralNative` has no recipient slot.\n\n**Unsupported in any single tx**\n\n- Init Capital — position NFT swept to operator.\n- Euler V2 / Silo V2/V3 native deposit paths — router/orchestrator credits msg.sender.\n- Fluid repay — on-chain primitive permits it, direct handler doesn't expose a receiver slot yet (routes to proxy as a conservative default).\n\nWhen the lender cannot honor a custom receiver on the direct path, `getTarget` falls back to `proxy` automatically. Forcing `mode=direct` on an unsupported path either throws (Gearbox credit-side, Aave V4 spoke, the explicit Silo router check) or silently credits the operator instead (Compound V2, Init, and Fluid fresh-open)."},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Withdraw/repay full balance.\n\nWhen on-chain position data is available, the API compares `amount` against the actual balance to decide the effective mode:\n\n- If `amount` covers the full position the action is treated as \"withdraw/repay all\" regardless of this flag.\n- If `isAll` is true but `amount` is less than the position, the API falls back to a partial action with the provided `amount` to avoid on-chain reverts.\n\nWithout on-chain data, the flag is trusted as-is."},{"name":"receiveAsset","in":"query","schema":{"type":"string"},"description":"Asset to receive.\n\nUse the zero address to receive native ETH on lenders whose vault returns native (e.g. Init Capital, Fluid native-ETH vaults, CompoundV2 `cETH` markets).\n\nProxy mode: defaults to market asset. Native delivery is supported only when the lender does so on-chain (e.g. Fluid, CompoundV2); otherwise the composer rejects."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"Amount is in shares (direct mode)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Per-position identifier. Lender-specific:\n\n- **Init Capital** — account ID, required for borrow/withdraw/repay.\n- **Euler V2** — sub-account index (0..255), defaults to 0.\n- **Gearbox V3** — credit-account address (alias: `creditAccount`).\n- **Fluid** — position NFT id. Omit or pass `0` to mint a new position; supply an existing nftId to act on an existing position. Required for borrow/withdraw/repay; for deposit it unlocks the direct path with a custom `receiver` (deposit lands on the receiver-owned NFT after pre-flight `ownerOf` validation)."},{"name":"slippage","in":"query","schema":{"type":"number"},"description":"Slippage tolerance in **basis points** (`50` = 0.5%, `100` = 1%).\n\nRequired whenever the action has to price something:\n\n- **Aggregator swap** — when `payAsset` / `receiveAsset` differs from the\n  market's underlying, the currency conversion is routed through a DEX\n  aggregator and this bounds that swap.\n- **Order-book take (Morpho Midnight)** — bounds the worst execution price\n  accepted while filling offers.\n\nIgnored when neither applies (same-asset action on a pool lender).","example":50},{"name":"simulate","in":"query","schema":{"type":"boolean"},"description":"When true, the API fetches the user's on-chain balances and returns projected post-trade metrics in the `simulation` field. Default: false."}],"responses":{"200":{"description":"Transaction calldata and approvals for withdraw","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/LendingRawResponse"},{"$ref":"#/components/schemas/LendingProxyResponse"}],"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Actions › Lending"],"summary":"Withdraw (simulate)","description":"Build calldata for withdrawing from a lending pool and simulate post-trade state. Same parameters as GET. Optionally send a JSON body with current portfolio state (`balanceData`, `aprData`, `positions`) to receive projected post-trade metrics in the `simulation` field — if omitted, the API fetches balances on-chain automatically. Use the data returned by the user-positions endpoint directly — always include `positions` for accurate health-factor and borrow-capacity projections.","parameters":[{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier (`lender:chainId:address`). The address encodes the underlying token (Aave/Morpho/CompoundV3), cToken (CompoundV2), or pool (Init Capital).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"mode","in":"query","schema":{"type":"string","enum":["proxy","direct"],"default":"direct"},"description":"Execution mode. proxy = 1delta composer, direct = raw protocol"},{"name":"operator","in":"query","schema":{"type":"string"},"description":"Wallet address of the user executing the action","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient of the lender position (deposit) or underlying tokens (borrow/withdraw).\nDefaults to `operator` when omitted.\n\nCustom-receiver support is per-lender. The API auto-selects the execution path:\n\n**Direct path supported**\n\n- Aave V2/V3 — `supply` / `withdraw` honor `to`.\n- Aave V4 — deposit via GiverPM `supplyOnBehalfOf`. Requires the GiverPM to be curated in `aave-v4-peripherals.json` with a name containing \"giver\".\n- Compound V3 — `supplyTo` / `withdrawTo`.\n- Morpho Blue & Lista DAO ERC-20 markets — Morpho `onBehalf`.\n- Euler V2 ERC-20 vaults — ERC-4626 `deposit(receiver)`. Collateral auto-enable is dropped for non-sub-account receivers.\n- Silo V2/V3 ERC-20 — `deposit(_, receiver, _)`.\n- Gearbox V3 passive-pool deposits — ERC-4626 `deposit(_, receiver)`.\n- Fluid borrow & withdraw — `operate(..., to_)` outflow slot.\n- Fluid **deposit to an existing NFT** when `accountId` is supplied — pure-deposit `operate()` calls bypass Fluid's owner auth gate, and the API folds a pre-flight `VaultFactory.ownerOf(nftId) == receiver` check into the merged multicall (rejects with 400 `INVALID_PARAM` on mismatch, `VALIDATION_FAILED` if the RPC can't confirm ownership).\n\n**Routes to composer (proxy) path**\n\n- Compound V2 family — direct `mint` is `msg.sender`-only.\n- Fluid **fresh-open deposits** — no `accountId` or `accountId=0`. The composer mints the new position NFT to itself, then transfers it to `receiver` via the encoder's `nftReceiver` slot.\n- Native-ETH vaults on Fluid (deposit / withdraw / repay) and the CompoundV2 family — the composer forwards `msg.value` to the vault's payable entrypoint. Other lenders' composer paths reject native asset and require wrapped-native as `payAsset`.\n\n**Direct-only branches that throw**\n\n- Gearbox V3 credit-side `addCollateral` — CA bound to operator.\n- Aave V4 native-gateway path — `supplyAsCollateralNative` has no recipient slot.\n\n**Unsupported in any single tx**\n\n- Init Capital — position NFT swept to operator.\n- Euler V2 / Silo V2/V3 native deposit paths — router/orchestrator credits msg.sender.\n- Fluid repay — on-chain primitive permits it, direct handler doesn't expose a receiver slot yet (routes to proxy as a conservative default).\n\nWhen the lender cannot honor a custom receiver on the direct path, `getTarget` falls back to `proxy` automatically. Forcing `mode=direct` on an unsupported path either throws (Gearbox credit-side, Aave V4 spoke, the explicit Silo router check) or silently credits the operator instead (Compound V2, Init, and Fluid fresh-open)."},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Withdraw/repay full balance.\n\nWhen on-chain position data is available, the API compares `amount` against the actual balance to decide the effective mode:\n\n- If `amount` covers the full position the action is treated as \"withdraw/repay all\" regardless of this flag.\n- If `isAll` is true but `amount` is less than the position, the API falls back to a partial action with the provided `amount` to avoid on-chain reverts.\n\nWithout on-chain data, the flag is trusted as-is."},{"name":"receiveAsset","in":"query","schema":{"type":"string"},"description":"Asset to receive.\n\nUse the zero address to receive native ETH on lenders whose vault returns native (e.g. Init Capital, Fluid native-ETH vaults, CompoundV2 `cETH` markets).\n\nProxy mode: defaults to market asset. Native delivery is supported only when the lender does so on-chain (e.g. Fluid, CompoundV2); otherwise the composer rejects."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"Amount is in shares (direct mode)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Per-position identifier. Lender-specific:\n\n- **Init Capital** — account ID, required for borrow/withdraw/repay.\n- **Euler V2** — sub-account index (0..255), defaults to 0.\n- **Gearbox V3** — credit-account address (alias: `creditAccount`).\n- **Fluid** — position NFT id. Omit or pass `0` to mint a new position; supply an existing nftId to act on an existing position. Required for borrow/withdraw/repay; for deposit it unlocks the direct path with a custom `receiver` (deposit lands on the receiver-owned NFT after pre-flight `ownerOf` validation)."},{"name":"slippage","in":"query","schema":{"type":"number"},"description":"Slippage tolerance in **basis points** (`50` = 0.5%, `100` = 1%).\n\nRequired whenever the action has to price something:\n\n- **Aggregator swap** — when `payAsset` / `receiveAsset` differs from the\n  market's underlying, the currency conversion is routed through a DEX\n  aggregator and this bounds that swap.\n- **Order-book take (Morpho Midnight)** — bounds the worst execution price\n  accepted while filling offers.\n\nIgnored when neither applies (same-asset action on a pool lender).","example":50},{"name":"simulate","in":"query","schema":{"type":"boolean"},"description":"When true, the API fetches the user's on-chain balances and returns projected post-trade metrics in the `simulation` field. Default: false."}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. If omitted, the API fetches balances on-chain automatically (slower, no `simulation` in response). When provided, pass `balanceData`, `aprData`, and `positions` directly from the matching sub-account in the `/v1/data/lending/user-positions` response — see the `SimulationBody` schema for a step-by-step example.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationBody"}}}},"responses":{"200":{"description":"Transaction calldata with post-trade simulation","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/LendingRawSimulationResponse"},{"$ref":"#/components/schemas/LendingProxySimulationResponse"}],"description":"Simulation results including projected health factor and borrow capacity"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/lending/borrow":{"get":{"tags":["Actions › Lending"],"summary":"Borrow","operationId":"lending-borrow","description":"Build calldata for borrowing from a lending pool. Identify the market via `marketUid` (format: `lender:chainId:address`). Approval transactions are automatically filtered: if the user already has sufficient allowances, `permissions` (envelope) and `permissionTxns` (per-entry) will be empty. Pass `simulate=true` to include projected post-trade metrics.\n\n**Lista DAO fixed-term (brokered) markets:** when the market is brokered (its public data has a non-empty `terms[]` rate card and `flags.variableBorrowDisabled === true`), pass `termId` to open a fixed-rate, fixed-term loan at the chosen term. Omitting `termId` on a brokered market opens the flexible (variable) position — direct mode only, since the broker has no on-behalf flex-borrow overload for the composer path.\n\n**Morpho Midnight order-book markets (`MORPHO_MIDNIGHT_<id>`):** borrowing = **TAKING** the bid side of the book — this endpoint fills the `offers` returned by [`/v1/data/lending/latest?includeOffers=true`](#operation/lending-latest), best-first. Borrowing is fixed-rate / fixed-maturity but has no `termId` (a single calendar maturity per market). To post your own limit offer instead (MAKE), use [`/v1/actions/midnight/make`](#operation/midnight-make).","parameters":[{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier (`lender:chainId:address`). The address encodes the underlying token (Aave/Morpho/CompoundV3), cToken (CompoundV2), or pool (Init Capital).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"mode","in":"query","schema":{"type":"string","enum":["proxy","direct"],"default":"direct"},"description":"Execution mode. proxy = 1delta composer, direct = raw protocol"},{"name":"operator","in":"query","schema":{"type":"string"},"description":"Wallet address of the user executing the action","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient of the lender position (deposit) or underlying tokens (borrow/withdraw).\nDefaults to `operator` when omitted.\n\nCustom-receiver support is per-lender. The API auto-selects the execution path:\n\n**Direct path supported**\n\n- Aave V2/V3 — `supply` / `withdraw` honor `to`.\n- Aave V4 — deposit via GiverPM `supplyOnBehalfOf`. Requires the GiverPM to be curated in `aave-v4-peripherals.json` with a name containing \"giver\".\n- Compound V3 — `supplyTo` / `withdrawTo`.\n- Morpho Blue & Lista DAO ERC-20 markets — Morpho `onBehalf`.\n- Euler V2 ERC-20 vaults — ERC-4626 `deposit(receiver)`. Collateral auto-enable is dropped for non-sub-account receivers.\n- Silo V2/V3 ERC-20 — `deposit(_, receiver, _)`.\n- Gearbox V3 passive-pool deposits — ERC-4626 `deposit(_, receiver)`.\n- Fluid borrow & withdraw — `operate(..., to_)` outflow slot.\n- Fluid **deposit to an existing NFT** when `accountId` is supplied — pure-deposit `operate()` calls bypass Fluid's owner auth gate, and the API folds a pre-flight `VaultFactory.ownerOf(nftId) == receiver` check into the merged multicall (rejects with 400 `INVALID_PARAM` on mismatch, `VALIDATION_FAILED` if the RPC can't confirm ownership).\n\n**Routes to composer (proxy) path**\n\n- Compound V2 family — direct `mint` is `msg.sender`-only.\n- Fluid **fresh-open deposits** — no `accountId` or `accountId=0`. The composer mints the new position NFT to itself, then transfers it to `receiver` via the encoder's `nftReceiver` slot.\n- Native-ETH vaults on Fluid (deposit / withdraw / repay) and the CompoundV2 family — the composer forwards `msg.value` to the vault's payable entrypoint. Other lenders' composer paths reject native asset and require wrapped-native as `payAsset`.\n\n**Direct-only branches that throw**\n\n- Gearbox V3 credit-side `addCollateral` — CA bound to operator.\n- Aave V4 native-gateway path — `supplyAsCollateralNative` has no recipient slot.\n\n**Unsupported in any single tx**\n\n- Init Capital — position NFT swept to operator.\n- Euler V2 / Silo V2/V3 native deposit paths — router/orchestrator credits msg.sender.\n- Fluid repay — on-chain primitive permits it, direct handler doesn't expose a receiver slot yet (routes to proxy as a conservative default).\n\nWhen the lender cannot honor a custom receiver on the direct path, `getTarget` falls back to `proxy` automatically. Forcing `mode=direct` on an unsupported path either throws (Gearbox credit-side, Aave V4 spoke, the explicit Silo router check) or silently credits the operator instead (Compound V2, Init, and Fluid fresh-open)."},{"name":"lendingMode","in":"query","schema":{"type":"string"},"description":"Interest rate mode (0=NONE, 1=STABLE, 2=VARIABLE)"},{"name":"termId","in":"query","schema":{"type":"integer"},"description":"**Lista DAO fixed-term (brokered) markets only.** Selects a fixed-rate term from the\nmarket's `terms[]` rate card (the `termId` of the chosen entry).\n\nA market is brokered when its public data carries a non-empty `terms[]` array and\n`flags.variableBorrowDisabled === true`; the broker contract is the mandatory gateway for the\ndebt side. When `termId` is supplied the borrow opens a fixed-term loan at that term; when it\nis omitted on a brokered market the borrow falls back to the flexible (variable) position\n(direct mode only — the composer/proxy path has no on-behalf flex-borrow and requires a term).\n\nIgnored for non-brokered lenders.","example":2},{"name":"receiveAsset","in":"query","schema":{"type":"string"},"description":"Asset to receive.\n\nUse the zero address to receive native ETH on lenders that allow native-debt borrows (e.g. Init Capital, Fluid native-debt vaults).\n\nProxy mode: defaults to market asset."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"Amount is in shares (direct mode)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Per-position identifier. Lender-specific:\n\n- **Init Capital** — account ID, required for borrow/withdraw/repay.\n- **Euler V2** — sub-account index (0..255), defaults to 0.\n- **Gearbox V3** — credit-account address (alias: `creditAccount`).\n- **Fluid** — position NFT id. Omit or pass `0` to mint a new position; supply an existing nftId to act on an existing position. Required for borrow/withdraw/repay; for deposit it unlocks the direct path with a custom `receiver` (deposit lands on the receiver-owned NFT after pre-flight `ownerOf` validation)."},{"name":"slippage","in":"query","schema":{"type":"number"},"description":"Slippage tolerance in **basis points** (`50` = 0.5%, `100` = 1%).\n\nRequired whenever the action has to price something:\n\n- **Aggregator swap** — when `payAsset` / `receiveAsset` differs from the\n  market's underlying, the currency conversion is routed through a DEX\n  aggregator and this bounds that swap.\n- **Order-book take (Morpho Midnight)** — bounds the worst execution price\n  accepted while filling offers.\n\nIgnored when neither applies (same-asset action on a pool lender).","example":50},{"name":"simulate","in":"query","schema":{"type":"boolean"},"description":"When true, the API fetches the user's on-chain balances and returns projected post-trade metrics in the `simulation` field. Default: false."}],"responses":{"200":{"description":"Transaction calldata and approvals for borrow","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/LendingRawResponse"},{"$ref":"#/components/schemas/LendingProxyResponse"}],"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Actions › Lending"],"summary":"Borrow (simulate)","description":"Build calldata for borrowing from a lending pool and simulate post-trade state. Same parameters as GET. Optionally send a JSON body with current portfolio state (`balanceData`, `aprData`, `positions`) to receive projected post-trade metrics in the `simulation` field — if omitted, the API fetches balances on-chain automatically. Use the data returned by the user-positions endpoint directly — always include `positions` for accurate health-factor and borrow-capacity projections.","parameters":[{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier (`lender:chainId:address`). The address encodes the underlying token (Aave/Morpho/CompoundV3), cToken (CompoundV2), or pool (Init Capital).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"mode","in":"query","schema":{"type":"string","enum":["proxy","direct"],"default":"direct"},"description":"Execution mode. proxy = 1delta composer, direct = raw protocol"},{"name":"operator","in":"query","schema":{"type":"string"},"description":"Wallet address of the user executing the action","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient of the lender position (deposit) or underlying tokens (borrow/withdraw).\nDefaults to `operator` when omitted.\n\nCustom-receiver support is per-lender. The API auto-selects the execution path:\n\n**Direct path supported**\n\n- Aave V2/V3 — `supply` / `withdraw` honor `to`.\n- Aave V4 — deposit via GiverPM `supplyOnBehalfOf`. Requires the GiverPM to be curated in `aave-v4-peripherals.json` with a name containing \"giver\".\n- Compound V3 — `supplyTo` / `withdrawTo`.\n- Morpho Blue & Lista DAO ERC-20 markets — Morpho `onBehalf`.\n- Euler V2 ERC-20 vaults — ERC-4626 `deposit(receiver)`. Collateral auto-enable is dropped for non-sub-account receivers.\n- Silo V2/V3 ERC-20 — `deposit(_, receiver, _)`.\n- Gearbox V3 passive-pool deposits — ERC-4626 `deposit(_, receiver)`.\n- Fluid borrow & withdraw — `operate(..., to_)` outflow slot.\n- Fluid **deposit to an existing NFT** when `accountId` is supplied — pure-deposit `operate()` calls bypass Fluid's owner auth gate, and the API folds a pre-flight `VaultFactory.ownerOf(nftId) == receiver` check into the merged multicall (rejects with 400 `INVALID_PARAM` on mismatch, `VALIDATION_FAILED` if the RPC can't confirm ownership).\n\n**Routes to composer (proxy) path**\n\n- Compound V2 family — direct `mint` is `msg.sender`-only.\n- Fluid **fresh-open deposits** — no `accountId` or `accountId=0`. The composer mints the new position NFT to itself, then transfers it to `receiver` via the encoder's `nftReceiver` slot.\n- Native-ETH vaults on Fluid (deposit / withdraw / repay) and the CompoundV2 family — the composer forwards `msg.value` to the vault's payable entrypoint. Other lenders' composer paths reject native asset and require wrapped-native as `payAsset`.\n\n**Direct-only branches that throw**\n\n- Gearbox V3 credit-side `addCollateral` — CA bound to operator.\n- Aave V4 native-gateway path — `supplyAsCollateralNative` has no recipient slot.\n\n**Unsupported in any single tx**\n\n- Init Capital — position NFT swept to operator.\n- Euler V2 / Silo V2/V3 native deposit paths — router/orchestrator credits msg.sender.\n- Fluid repay — on-chain primitive permits it, direct handler doesn't expose a receiver slot yet (routes to proxy as a conservative default).\n\nWhen the lender cannot honor a custom receiver on the direct path, `getTarget` falls back to `proxy` automatically. Forcing `mode=direct` on an unsupported path either throws (Gearbox credit-side, Aave V4 spoke, the explicit Silo router check) or silently credits the operator instead (Compound V2, Init, and Fluid fresh-open)."},{"name":"lendingMode","in":"query","schema":{"type":"string"},"description":"Interest rate mode (0=NONE, 1=STABLE, 2=VARIABLE)"},{"name":"termId","in":"query","schema":{"type":"integer"},"description":"**Lista DAO fixed-term (brokered) markets only.** Selects a fixed-rate term from the\nmarket's `terms[]` rate card (the `termId` of the chosen entry).\n\nA market is brokered when its public data carries a non-empty `terms[]` array and\n`flags.variableBorrowDisabled === true`; the broker contract is the mandatory gateway for the\ndebt side. When `termId` is supplied the borrow opens a fixed-term loan at that term; when it\nis omitted on a brokered market the borrow falls back to the flexible (variable) position\n(direct mode only — the composer/proxy path has no on-behalf flex-borrow and requires a term).\n\nIgnored for non-brokered lenders.","example":2},{"name":"receiveAsset","in":"query","schema":{"type":"string"},"description":"Asset to receive.\n\nUse the zero address to receive native ETH on lenders that allow native-debt borrows (e.g. Init Capital, Fluid native-debt vaults).\n\nProxy mode: defaults to market asset."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"Amount is in shares (direct mode)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Per-position identifier. Lender-specific:\n\n- **Init Capital** — account ID, required for borrow/withdraw/repay.\n- **Euler V2** — sub-account index (0..255), defaults to 0.\n- **Gearbox V3** — credit-account address (alias: `creditAccount`).\n- **Fluid** — position NFT id. Omit or pass `0` to mint a new position; supply an existing nftId to act on an existing position. Required for borrow/withdraw/repay; for deposit it unlocks the direct path with a custom `receiver` (deposit lands on the receiver-owned NFT after pre-flight `ownerOf` validation)."},{"name":"slippage","in":"query","schema":{"type":"number"},"description":"Slippage tolerance in **basis points** (`50` = 0.5%, `100` = 1%).\n\nRequired whenever the action has to price something:\n\n- **Aggregator swap** — when `payAsset` / `receiveAsset` differs from the\n  market's underlying, the currency conversion is routed through a DEX\n  aggregator and this bounds that swap.\n- **Order-book take (Morpho Midnight)** — bounds the worst execution price\n  accepted while filling offers.\n\nIgnored when neither applies (same-asset action on a pool lender).","example":50},{"name":"simulate","in":"query","schema":{"type":"boolean"},"description":"When true, the API fetches the user's on-chain balances and returns projected post-trade metrics in the `simulation` field. Default: false."}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. If omitted, the API fetches balances on-chain automatically (slower, no `simulation` in response). When provided, pass `balanceData`, `aprData`, and `positions` directly from the matching sub-account in the `/v1/data/lending/user-positions` response — see the `SimulationBody` schema for a step-by-step example.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationBody"}}}},"responses":{"200":{"description":"Transaction calldata with post-trade simulation","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/LendingRawSimulationResponse"},{"$ref":"#/components/schemas/LendingProxySimulationResponse"}],"description":"Simulation results including projected health factor and borrow capacity"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/lending/repay":{"get":{"tags":["Actions › Lending"],"summary":"Repay","operationId":"lending-repay","description":"Build calldata for repaying a loan. Identify the market via `marketUid` (format: `lender:chainId:address`). Approval transactions are automatically filtered: if the user already has sufficient allowances, `permissions` (envelope) and `permissionTxns` (per-entry) will be empty. Pass `simulate=true` to include projected post-trade metrics. When on-chain debt data is available and `amount` covers the full debt, the API uses protocol-level max-repay mechanisms automatically. If `isAll=true` but `amount` is less than the debt, the API falls back to a partial repay to avoid reverts.\n\n**Lista DAO fixed-term (brokered) markets:** pass `loanId` to target a specific loan (use the per-loan `loanId` from the user-positions response, or the `type(uint128).max` sentinel for the flexible/dynamic position). The broker repays interest-first plus an early-repayment penalty on not-yet-matured fixed loans and refunds any excess, so to fully close a loan fund `outstanding + accruedInterest + earlyRepayPenalty` from the per-loan `term`.\n\n**Morpho Midnight fixed-term markets (`MORPHO_MIDNIGHT_<id>`) — repaying to avoid liquidation.** A Midnight loan is **zero-coupon and fixed-maturity**: you owe a *static face value* — the position's `debt` in loan-token units — repaid **1:1**, which does **not** grow over time. So the amount to repay is fixed and known up front (the continuous + settlement fees accrue on the *lender's* side and are **never** added to your debt), and there is **no early-repayment penalty** (unlike Lista) — you may repay any time before maturity at face value.\n\nA borrower faces **two** liquidation triggers; both are cleared by repaying:\n\n- **Before maturity — health (LTV) based.** As with any collateralised loan, if the collateral's oracle value falls enough to breach the market's LLTV, the position can be liquidated. Keep an LTV buffer, add collateral, or repay down.\n- **At/after maturity — default.** The fixed `maturity` (surfaced on `params.market.maturity`) is a **hard deadline**. Once it passes, an unrepaid loan is **in default and can be liquidated regardless of health or LTV** — being past-due is itself the trigger. This is the Midnight-specific risk to watch: mark the maturity date and fully repay **before** it, even if the position is comfortably collateralised.\n\n**Repay exactly the debt.** Over-repaying **reverts** on-chain (there is no over-repay buffer — do **not** pad the `amount`, and `isAll` does not auto-size here), while under-repaying leaves a dust position that stays open and therefore still liquidatable after maturity. Set `amount` to the position's current `debt` (loan-token units) from `/v1/data/lending/user-positions` and approve exactly that to the Midnight core. To **fully close and reclaim collateral**, repay the debt and then withdraw the collateral via [`/v1/actions/lending/withdraw`](#operation/lending-withdraw) — collateral cannot be freed while any debt remains.\n\n**Teller markets (`TELLER_<pool>`):** pass `posId` = the `bidId` to repay (from the user-positions `term.loanId`). A **full** repay (`isAll`, or an `amount` ≥ the amount owed) uses `repayLoanFull` which repays principal + interest **and releases ALL the bid's collateral** — Teller has no keep-collateral partial close. A **partial** repay (`amount` < owed) uses `repayLoan` and keeps the collateral escrowed. ⚠ Liquidation is TIME-based and **aggressive**: after the term you have only a **short grace window** (`params.market.teller.paymentDefaultDuration`, as low as 5 min) to roll over or repay — miss it and the loan DEFAULTS, and a liquidator can seize your **ENTIRE collateral** (not just the amount owed). Repay or roll over **before** the deadline; there is no price buffer.","parameters":[{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier (`lender:chainId:address`). The address encodes the underlying token (Aave/Morpho/CompoundV3), cToken (CompoundV2), or pool (Init Capital).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"mode","in":"query","schema":{"type":"string","enum":["proxy","direct"],"default":"direct"},"description":"Execution mode. proxy = 1delta composer, direct = raw protocol"},{"name":"operator","in":"query","schema":{"type":"string"},"description":"Wallet address of the user executing the action","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient of the lender position (deposit) or underlying tokens (borrow/withdraw).\nDefaults to `operator` when omitted.\n\nCustom-receiver support is per-lender. The API auto-selects the execution path:\n\n**Direct path supported**\n\n- Aave V2/V3 — `supply` / `withdraw` honor `to`.\n- Aave V4 — deposit via GiverPM `supplyOnBehalfOf`. Requires the GiverPM to be curated in `aave-v4-peripherals.json` with a name containing \"giver\".\n- Compound V3 — `supplyTo` / `withdrawTo`.\n- Morpho Blue & Lista DAO ERC-20 markets — Morpho `onBehalf`.\n- Euler V2 ERC-20 vaults — ERC-4626 `deposit(receiver)`. Collateral auto-enable is dropped for non-sub-account receivers.\n- Silo V2/V3 ERC-20 — `deposit(_, receiver, _)`.\n- Gearbox V3 passive-pool deposits — ERC-4626 `deposit(_, receiver)`.\n- Fluid borrow & withdraw — `operate(..., to_)` outflow slot.\n- Fluid **deposit to an existing NFT** when `accountId` is supplied — pure-deposit `operate()` calls bypass Fluid's owner auth gate, and the API folds a pre-flight `VaultFactory.ownerOf(nftId) == receiver` check into the merged multicall (rejects with 400 `INVALID_PARAM` on mismatch, `VALIDATION_FAILED` if the RPC can't confirm ownership).\n\n**Routes to composer (proxy) path**\n\n- Compound V2 family — direct `mint` is `msg.sender`-only.\n- Fluid **fresh-open deposits** — no `accountId` or `accountId=0`. The composer mints the new position NFT to itself, then transfers it to `receiver` via the encoder's `nftReceiver` slot.\n- Native-ETH vaults on Fluid (deposit / withdraw / repay) and the CompoundV2 family — the composer forwards `msg.value` to the vault's payable entrypoint. Other lenders' composer paths reject native asset and require wrapped-native as `payAsset`.\n\n**Direct-only branches that throw**\n\n- Gearbox V3 credit-side `addCollateral` — CA bound to operator.\n- Aave V4 native-gateway path — `supplyAsCollateralNative` has no recipient slot.\n\n**Unsupported in any single tx**\n\n- Init Capital — position NFT swept to operator.\n- Euler V2 / Silo V2/V3 native deposit paths — router/orchestrator credits msg.sender.\n- Fluid repay — on-chain primitive permits it, direct handler doesn't expose a receiver slot yet (routes to proxy as a conservative default).\n\nWhen the lender cannot honor a custom receiver on the direct path, `getTarget` falls back to `proxy` automatically. Forcing `mode=direct` on an unsupported path either throws (Gearbox credit-side, Aave V4 spoke, the explicit Silo router check) or silently credits the operator instead (Compound V2, Init, and Fluid fresh-open)."},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Withdraw/repay full balance.\n\nWhen on-chain position data is available, the API compares `amount` against the actual balance to decide the effective mode:\n\n- If `amount` covers the full position the action is treated as \"withdraw/repay all\" regardless of this flag.\n- If `isAll` is true but `amount` is less than the position, the API falls back to a partial action with the provided `amount` to avoid on-chain reverts.\n\nWithout on-chain data, the flag is trusted as-is."},{"name":"lendingMode","in":"query","schema":{"type":"string"},"description":"Interest rate mode (0=NONE, 1=STABLE, 2=VARIABLE)"},{"name":"loanId","in":"query","schema":{"type":"string"},"description":"**Lista DAO fixed-term (brokered) markets only.** Identifies which loan to repay.\n\nPass the `loanId` of the target loan from the user's positions (each per-loan position in the\nuser-positions response carries `loanId` and a `term` object). To repay the flexible /\ndynamic position instead, pass the sentinel `340282366920938463463374607431768211455`\n(`type(uint128).max`).\n\nThe broker repays interest-first (plus an early-repayment penalty for not-yet-matured fixed\nloans) and refunds any excess to the caller, so to fully close a loan fund\n`outstanding + accruedInterest + earlyRepayPenalty` (all on the per-loan `term`).\n\nRequired on brokered repays; ignored for non-brokered lenders.","example":"450"},{"name":"payAsset","in":"query","schema":{"type":"string"},"description":"Asset to pay with.\n\nUse the zero address to pay with native ETH on lenders whose vault accepts native debt repayment (e.g. Init Capital, Fluid native-debt vaults, CompoundV2 `cETH` markets).\n\nProxy mode: defaults to market asset. Native is forwarded as `msg.value` only when the lender supports it (e.g. Fluid, CompoundV2); otherwise the composer rejects."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"Amount is in shares (direct mode)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Per-position identifier. Lender-specific:\n\n- **Init Capital** — account ID, required for borrow/withdraw/repay.\n- **Euler V2** — sub-account index (0..255), defaults to 0.\n- **Gearbox V3** — credit-account address (alias: `creditAccount`).\n- **Fluid** — position NFT id. Omit or pass `0` to mint a new position; supply an existing nftId to act on an existing position. Required for borrow/withdraw/repay; for deposit it unlocks the direct path with a custom `receiver` (deposit lands on the receiver-owned NFT after pre-flight `ownerOf` validation)."},{"name":"slippage","in":"query","schema":{"type":"number"},"description":"Slippage tolerance in **basis points** (`50` = 0.5%, `100` = 1%).\n\nRequired whenever the action has to price something:\n\n- **Aggregator swap** — when `payAsset` / `receiveAsset` differs from the\n  market's underlying, the currency conversion is routed through a DEX\n  aggregator and this bounds that swap.\n- **Order-book take (Morpho Midnight)** — bounds the worst execution price\n  accepted while filling offers.\n\nIgnored when neither applies (same-asset action on a pool lender).","example":50},{"name":"simulate","in":"query","schema":{"type":"boolean"},"description":"When true, the API fetches the user's on-chain balances and returns projected post-trade metrics in the `simulation` field. Default: false."}],"responses":{"200":{"description":"Transaction calldata and approvals for repay","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/LendingRawResponse"},{"$ref":"#/components/schemas/LendingProxyResponse"}],"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Actions › Lending"],"summary":"Repay (simulate)","description":"Build calldata for repaying a loan and simulate post-trade state. Same parameters as GET. Optionally send a JSON body with current portfolio state (`balanceData`, `aprData`, `positions`) to receive projected post-trade metrics in the `simulation` field — if omitted, the API fetches balances on-chain automatically. Use the data returned by the user-positions endpoint directly — always include `positions` for accurate health-factor and borrow-capacity projections.","parameters":[{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier (`lender:chainId:address`). The address encodes the underlying token (Aave/Morpho/CompoundV3), cToken (CompoundV2), or pool (Init Capital).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"mode","in":"query","schema":{"type":"string","enum":["proxy","direct"],"default":"direct"},"description":"Execution mode. proxy = 1delta composer, direct = raw protocol"},{"name":"operator","in":"query","schema":{"type":"string"},"description":"Wallet address of the user executing the action","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient of the lender position (deposit) or underlying tokens (borrow/withdraw).\nDefaults to `operator` when omitted.\n\nCustom-receiver support is per-lender. The API auto-selects the execution path:\n\n**Direct path supported**\n\n- Aave V2/V3 — `supply` / `withdraw` honor `to`.\n- Aave V4 — deposit via GiverPM `supplyOnBehalfOf`. Requires the GiverPM to be curated in `aave-v4-peripherals.json` with a name containing \"giver\".\n- Compound V3 — `supplyTo` / `withdrawTo`.\n- Morpho Blue & Lista DAO ERC-20 markets — Morpho `onBehalf`.\n- Euler V2 ERC-20 vaults — ERC-4626 `deposit(receiver)`. Collateral auto-enable is dropped for non-sub-account receivers.\n- Silo V2/V3 ERC-20 — `deposit(_, receiver, _)`.\n- Gearbox V3 passive-pool deposits — ERC-4626 `deposit(_, receiver)`.\n- Fluid borrow & withdraw — `operate(..., to_)` outflow slot.\n- Fluid **deposit to an existing NFT** when `accountId` is supplied — pure-deposit `operate()` calls bypass Fluid's owner auth gate, and the API folds a pre-flight `VaultFactory.ownerOf(nftId) == receiver` check into the merged multicall (rejects with 400 `INVALID_PARAM` on mismatch, `VALIDATION_FAILED` if the RPC can't confirm ownership).\n\n**Routes to composer (proxy) path**\n\n- Compound V2 family — direct `mint` is `msg.sender`-only.\n- Fluid **fresh-open deposits** — no `accountId` or `accountId=0`. The composer mints the new position NFT to itself, then transfers it to `receiver` via the encoder's `nftReceiver` slot.\n- Native-ETH vaults on Fluid (deposit / withdraw / repay) and the CompoundV2 family — the composer forwards `msg.value` to the vault's payable entrypoint. Other lenders' composer paths reject native asset and require wrapped-native as `payAsset`.\n\n**Direct-only branches that throw**\n\n- Gearbox V3 credit-side `addCollateral` — CA bound to operator.\n- Aave V4 native-gateway path — `supplyAsCollateralNative` has no recipient slot.\n\n**Unsupported in any single tx**\n\n- Init Capital — position NFT swept to operator.\n- Euler V2 / Silo V2/V3 native deposit paths — router/orchestrator credits msg.sender.\n- Fluid repay — on-chain primitive permits it, direct handler doesn't expose a receiver slot yet (routes to proxy as a conservative default).\n\nWhen the lender cannot honor a custom receiver on the direct path, `getTarget` falls back to `proxy` automatically. Forcing `mode=direct` on an unsupported path either throws (Gearbox credit-side, Aave V4 spoke, the explicit Silo router check) or silently credits the operator instead (Compound V2, Init, and Fluid fresh-open)."},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Withdraw/repay full balance.\n\nWhen on-chain position data is available, the API compares `amount` against the actual balance to decide the effective mode:\n\n- If `amount` covers the full position the action is treated as \"withdraw/repay all\" regardless of this flag.\n- If `isAll` is true but `amount` is less than the position, the API falls back to a partial action with the provided `amount` to avoid on-chain reverts.\n\nWithout on-chain data, the flag is trusted as-is."},{"name":"lendingMode","in":"query","schema":{"type":"string"},"description":"Interest rate mode (0=NONE, 1=STABLE, 2=VARIABLE)"},{"name":"loanId","in":"query","schema":{"type":"string"},"description":"**Lista DAO fixed-term (brokered) markets only.** Identifies which loan to repay.\n\nPass the `loanId` of the target loan from the user's positions (each per-loan position in the\nuser-positions response carries `loanId` and a `term` object). To repay the flexible /\ndynamic position instead, pass the sentinel `340282366920938463463374607431768211455`\n(`type(uint128).max`).\n\nThe broker repays interest-first (plus an early-repayment penalty for not-yet-matured fixed\nloans) and refunds any excess to the caller, so to fully close a loan fund\n`outstanding + accruedInterest + earlyRepayPenalty` (all on the per-loan `term`).\n\nRequired on brokered repays; ignored for non-brokered lenders.","example":"450"},{"name":"payAsset","in":"query","schema":{"type":"string"},"description":"Asset to pay with.\n\nUse the zero address to pay with native ETH on lenders whose vault accepts native debt repayment (e.g. Init Capital, Fluid native-debt vaults, CompoundV2 `cETH` markets).\n\nProxy mode: defaults to market asset. Native is forwarded as `msg.value` only when the lender supports it (e.g. Fluid, CompoundV2); otherwise the composer rejects."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"Amount is in shares (direct mode)"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Per-position identifier. Lender-specific:\n\n- **Init Capital** — account ID, required for borrow/withdraw/repay.\n- **Euler V2** — sub-account index (0..255), defaults to 0.\n- **Gearbox V3** — credit-account address (alias: `creditAccount`).\n- **Fluid** — position NFT id. Omit or pass `0` to mint a new position; supply an existing nftId to act on an existing position. Required for borrow/withdraw/repay; for deposit it unlocks the direct path with a custom `receiver` (deposit lands on the receiver-owned NFT after pre-flight `ownerOf` validation)."},{"name":"slippage","in":"query","schema":{"type":"number"},"description":"Slippage tolerance in **basis points** (`50` = 0.5%, `100` = 1%).\n\nRequired whenever the action has to price something:\n\n- **Aggregator swap** — when `payAsset` / `receiveAsset` differs from the\n  market's underlying, the currency conversion is routed through a DEX\n  aggregator and this bounds that swap.\n- **Order-book take (Morpho Midnight)** — bounds the worst execution price\n  accepted while filling offers.\n\nIgnored when neither applies (same-asset action on a pool lender).","example":50},{"name":"simulate","in":"query","schema":{"type":"boolean"},"description":"When true, the API fetches the user's on-chain balances and returns projected post-trade metrics in the `simulation` field. Default: false."}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. If omitted, the API fetches balances on-chain automatically (slower, no `simulation` in response). When provided, pass `balanceData`, `aprData`, and `positions` directly from the matching sub-account in the `/v1/data/lending/user-positions` response — see the `SimulationBody` schema for a step-by-step example.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationBody"}}}},"responses":{"200":{"description":"Transaction calldata with post-trade simulation","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/LendingRawSimulationResponse"},{"$ref":"#/components/schemas/LendingProxySimulationResponse"}],"description":"Simulation results including projected health factor and borrow capacity"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/lending/mode":{"get":{"tags":["Actions › Lending"],"summary":"Switch mode","description":"Build calldata for switching the active risk-category (\"mode\") on a lender that supports it (e.g. Aave V3 e-mode).\n\nThe \"mode\" terminology is the protocol-agnostic generalization of Aave V3's \"e-mode\" (efficiency mode); other lenders expose analogous category mechanisms.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"Protocol identifier","example":"AAVE_V3"},{"name":"mode","in":"query","required":true,"schema":{"type":"integer"},"description":"Target mode category ID","example":1}],"responses":{"200":{"description":"Transaction calldata","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"$ref":"#/components/schemas/LendingRawResponse","description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/lending/enable-collateral":{"get":{"tags":["Actions › Lending"],"summary":"Enable/disable collateral","description":"Build calldata for toggling an asset as collateral. Supported on lenders with explicit collateral toggles (Aave V2/V3, Compound V2 via `enterMarkets`/`exitMarket`); other lenders return a 400. Identify the market via `marketUid` (format: `lender:chainId:address`).","parameters":[{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier (`lender:chainId:address`). The address encodes the underlying token (Aave) or cToken (CompoundV2).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"enabled","in":"query","required":true,"schema":{"type":"boolean"},"description":"true to enable, false to disable","example":true}],"responses":{"200":{"description":"Transaction calldata","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"$ref":"#/components/schemas/LendingRawResponse","description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/lending/repay-with-atoken":{"get":{"tags":["Actions › Lending"],"summary":"Repay with aToken","description":"Build calldata for repaying a loan using aTokens on Aave V3. Other lenders return a 400. Identify the market via `marketUid` (format: `lender:chainId:address`).","parameters":[{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier (`lender:chainId:address`). The address encodes the underlying token.","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"lendingMode","in":"query","schema":{"type":"string"},"description":"Interest rate mode (default: VARIABLE)"}],"responses":{"200":{"description":"Transaction calldata","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"$ref":"#/components/schemas/LendingRawResponse","description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/loop/leverage":{"get":{"tags":["Actions › Loop"],"summary":"Leverage (loop)","description":"Open a leveraged position via borrow → swap → deposit. Identify the debt market with `marketUidIn` and collateral market with `marketUidOut`. Omit `account` for quote-only (returns `data.quotes` with price deltas). Include `account` to build full transaction calldata (populates `actions` with `alternatives`, `transactions`, and `permissions`).\n\n**Response `actions` fields (when `account` is provided):**\n- `alternatives`: DEX aggregator swap transactions sorted best-output-first. Pick one to execute.\n- `transactions`: Pre-trade setup — mode/e-mode switch (Aave V3) or collateral enable (Venus). Empty if not needed.\n- `permissions`: ERC20 approvals (targeting the composer contract) and lender borrow delegations (targeting the lending protocol contract). Only missing approvals are returned. Execute these first.\n\n**Lista DAO fixed-term (brokered) markets:** when `marketUidIn` is brokered, pass `termId` to open the loop's debt leg as a fixed-term broker loan. The collateral deposit leg is handled automatically (routed through the market's `collateralProvider` when set, e.g. slisBNB). Closing such a loop requires the loan's `loanId` on `/v1/actions/loop/close`.","parameters":[{"name":"route","in":"query","schema":{"type":"string","enum":["auto","bundler3","native","composer","proxy"]},"description":"Which assembly to use, for lenders that have more than one. **Morpho Blue only** today.\n\n- *(omitted)* — `auto`: prefer the protocol’s own tooling and fall back to the composer\n  for anything it cannot express (a cross-market pair, a margin paid in a foreign asset,\n  an `EXACT_OUTPUT` close, a chain with no bundler3 or no swap adapter).\n- `bundler3` / `native` — force Morpho’s bundler3. A request it cannot serve becomes an\n  ERROR instead of silently falling back, which is what makes the route testable.\n- `composer` / `proxy` — force the 1delta composer.\n\nThe default matters: the bundler3 route asks for `setAuthorization(GeneralAdapter1)`,\nMorpho’s own audited adapter, where the composer route asks for a standing grant on ours."},{"name":"marketUidIn","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for input side (`lender:chainId:address`).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for output side (`lender:chainId:address`).","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"slippage","in":"query","required":true,"schema":{"type":"number"},"description":"Slippage tolerance (basis points)","example":50},{"name":"account","in":"query","schema":{"type":"string"},"description":"Account address. Include to build transaction, omit for quote-only.","example":"0xYourAddress"},{"name":"debtAmount","in":"query","required":true,"schema":{"type":"string"},"description":"Debt amount in wei","example":"1000000000000000000"},{"name":"payAsset","in":"query","schema":{"type":"string"},"description":"Asset to pay with (optional zap-in asset)"},{"name":"payAmount","in":"query","schema":{"type":"string"},"description":"Pay amount in wei"},{"name":"marginFromAccountId","in":"query","schema":{"type":"integer"},"description":"**Dolomite only.** Fund the margin from this existing Dolomite sub-account\ninstead of the wallet, collapsing the open to ONE transaction: the transfer\nrides inside the leverage call and no deposit or token approval is emitted.\nOmit to pull `payAmount` from the wallet (two transactions — Dolomite's\ntrader proxy can only move funds between the caller's own sub-accounts).\n`0` is the default sub-account and a valid source. Passing the trade\naccount's own id is treated as absent, since the margin is already there.\nThe source must hold `payAmount` of the pay asset or the transaction reverts.","example":0},{"name":"leverage","in":"query","schema":{"type":"number"},"description":"Target leverage multiplier"},{"name":"usePendleMintRedeem","in":"query","schema":{"type":"boolean"},"description":"Use Pendle mint/redeem"},{"name":"borrowMode","in":"query","schema":{"type":"integer","enum":[0,1,2]},"description":"Borrow mode (0=NONE, 1=STABLE, 2=VARIABLE)"},{"name":"termId","in":"query","schema":{"type":"integer"},"description":"**Lista DAO fixed-term (brokered) debt markets only.** Selects the fixed term to borrow for\nthe loop's debt leg, from the debt market's `terms[]` rate card (the `MarketTerm.termId`).\nRequired when `marketUidIn` is a brokered market — the borrow leg routes through the broker;\nignored otherwise.","example":1},{"name":"posId","in":"query","schema":{"type":"string"},"description":"**Twyne only.** Which collateral vault to lever — a Twyne position is a\ncontract the borrower deploys, and one account may own several in a market,\neach with its own collateral, debt and liquidation LTV. Omit when the account\nowns exactly one (or none). Passing none while owning several is refused\nrather than guessed."},{"name":"liqLtv","in":"query","schema":{"type":"integer"},"description":"**Twyne only, and only when the account owns NO vault in this market yet.**\nThe liquidation LTV (1e4 scale) the position this call DEPLOYS will carry.\nThe deploy runs as the first item of the same EVC batch, so a first-time\nborrower opens a leveraged position in one transaction; the response then\ncarries `createdPosition` and a `collateralVault` address that does not exist\nuntil the transaction lands.\n\nDefaults to the market’s LIVE ceiling (`maxTwyneLTVs`) — the extra LTV is the\nreason to borrow here, and the value is not a commitment: `setTwyneLiqLTV`\nmoves it at any time with no cooldown, and lowering it RELEASES reserved\ncredit and lowers what the borrower pays. Must sit inside the live band\n(`externalLiqLTV × externalLiqBuffer` … `maxTwyneLTVs`); both bounds move, so\na value is validated against a fresh read and refused by name rather than\nreverting `ValueOutOfRange()` on chain.\n\nIgnored when a position already exists — moving an existing dial is\n`/v1/actions/twyne/set-liq-ltv`.","example":9700},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Account ID (Init Capital)"},{"name":"selectedMode","in":"query","schema":{"type":"integer"},"description":"Position mode for new positions (Init Capital)"}],"responses":{"200":{"description":"Quote or full build response","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/MarginQuoteResponse"},{"$ref":"#/components/schemas/MarginBuildResponse"}],"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Actions › Loop"],"summary":"Leverage loop (simulate)","description":"Open a leveraged position and simulate post-trade state. Same parameters as GET. Optionally send a JSON body with current portfolio state (`balanceData`, `aprData`, `positions`) to receive projected post-trade metrics in the `simulation` field — if omitted, the API fetches balances on-chain automatically. Use the data returned by the user-positions endpoint directly — always include `positions` for accurate health-factor and borrow-capacity projections.","parameters":[{"name":"route","in":"query","schema":{"type":"string","enum":["auto","bundler3","native","composer","proxy"]},"description":"Which assembly to use, for lenders that have more than one. **Morpho Blue only** today.\n\n- *(omitted)* — `auto`: prefer the protocol’s own tooling and fall back to the composer\n  for anything it cannot express (a cross-market pair, a margin paid in a foreign asset,\n  an `EXACT_OUTPUT` close, a chain with no bundler3 or no swap adapter).\n- `bundler3` / `native` — force Morpho’s bundler3. A request it cannot serve becomes an\n  ERROR instead of silently falling back, which is what makes the route testable.\n- `composer` / `proxy` — force the 1delta composer.\n\nThe default matters: the bundler3 route asks for `setAuthorization(GeneralAdapter1)`,\nMorpho’s own audited adapter, where the composer route asks for a standing grant on ours."},{"name":"marketUidIn","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for input side (`lender:chainId:address`).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for output side (`lender:chainId:address`).","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"slippage","in":"query","required":true,"schema":{"type":"number"},"description":"Slippage tolerance (basis points)","example":50},{"name":"account","in":"query","schema":{"type":"string"},"description":"Account address. Include to build transaction, omit for quote-only.","example":"0xYourAddress"},{"name":"debtAmount","in":"query","required":true,"schema":{"type":"string"},"description":"Debt amount in wei","example":"1000000000000000000"},{"name":"payAsset","in":"query","schema":{"type":"string"},"description":"Asset to pay with (optional zap-in asset)"},{"name":"payAmount","in":"query","schema":{"type":"string"},"description":"Pay amount in wei"},{"name":"marginFromAccountId","in":"query","schema":{"type":"integer"},"description":"**Dolomite only.** Fund the margin from this existing Dolomite sub-account\ninstead of the wallet, collapsing the open to ONE transaction: the transfer\nrides inside the leverage call and no deposit or token approval is emitted.\nOmit to pull `payAmount` from the wallet (two transactions — Dolomite's\ntrader proxy can only move funds between the caller's own sub-accounts).\n`0` is the default sub-account and a valid source. Passing the trade\naccount's own id is treated as absent, since the margin is already there.\nThe source must hold `payAmount` of the pay asset or the transaction reverts.","example":0},{"name":"leverage","in":"query","schema":{"type":"number"},"description":"Target leverage multiplier"},{"name":"usePendleMintRedeem","in":"query","schema":{"type":"boolean"},"description":"Use Pendle mint/redeem"},{"name":"borrowMode","in":"query","schema":{"type":"integer","enum":[0,1,2]},"description":"Borrow mode (0=NONE, 1=STABLE, 2=VARIABLE)"},{"name":"termId","in":"query","schema":{"type":"integer"},"description":"**Lista DAO fixed-term (brokered) debt markets only.** Selects the fixed term to borrow for\nthe loop's debt leg, from the debt market's `terms[]` rate card (the `MarketTerm.termId`).\nRequired when `marketUidIn` is a brokered market — the borrow leg routes through the broker;\nignored otherwise.","example":1},{"name":"posId","in":"query","schema":{"type":"string"},"description":"**Twyne only.** Which collateral vault to lever — a Twyne position is a\ncontract the borrower deploys, and one account may own several in a market,\neach with its own collateral, debt and liquidation LTV. Omit when the account\nowns exactly one (or none). Passing none while owning several is refused\nrather than guessed."},{"name":"liqLtv","in":"query","schema":{"type":"integer"},"description":"**Twyne only, and only when the account owns NO vault in this market yet.**\nThe liquidation LTV (1e4 scale) the position this call DEPLOYS will carry.\nThe deploy runs as the first item of the same EVC batch, so a first-time\nborrower opens a leveraged position in one transaction; the response then\ncarries `createdPosition` and a `collateralVault` address that does not exist\nuntil the transaction lands.\n\nDefaults to the market’s LIVE ceiling (`maxTwyneLTVs`) — the extra LTV is the\nreason to borrow here, and the value is not a commitment: `setTwyneLiqLTV`\nmoves it at any time with no cooldown, and lowering it RELEASES reserved\ncredit and lowers what the borrower pays. Must sit inside the live band\n(`externalLiqLTV × externalLiqBuffer` … `maxTwyneLTVs`); both bounds move, so\na value is validated against a fresh read and refused by name rather than\nreverting `ValueOutOfRange()` on chain.\n\nIgnored when a position already exists — moving an existing dial is\n`/v1/actions/twyne/set-liq-ltv`.","example":9700},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Account ID (Init Capital)"},{"name":"selectedMode","in":"query","schema":{"type":"integer"},"description":"Position mode for new positions (Init Capital)"}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. If omitted, the API fetches balances on-chain automatically (slower, no `simulation` in response). When provided, pass `balanceData`, `aprData`, and `positions` directly from the matching sub-account in the `/v1/data/lending/user-positions` response — see the `SimulationBody` schema for a step-by-step example.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationBody"}}}},"responses":{"200":{"description":"Quote or full build with simulation","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/MarginSimulationQuoteResponse"},{"$ref":"#/components/schemas/MarginSimulationBuildResponse"}],"description":"Quotes and simulation results"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/loop/close":{"get":{"tags":["Actions › Loop"],"summary":"Close margin position","description":"Close a leveraged position. Identify the collateral market with `marketUidIn` and debt market with `marketUidOut`. Omit `account` for quote-only (returns `data.quotes` with price deltas). Include `account` to build full transaction calldata (populates `actions` with `alternatives`, `transactions`, and `permissions`).\n\n**Response `actions` fields (when `account` is provided):**\n- `alternatives`: DEX aggregator swap transactions sorted best-output-first. Pick one to execute.\n- `transactions`: Empty for close operations (no setup needed).\n- `permissions`: Lender withdrawal delegations (targeting the lending protocol contract). Only missing delegations are returned. Execute these first.\n\n**Lista DAO fixed-term (brokered) markets:** when `marketUidOut` is brokered, pass `loanId` to select which broker loan the repay leg pays down (or the `type(uint128).max` sentinel for the flexible position). The collateral withdraw leg is handled automatically (routed through the market's `collateralProvider` when set, e.g. slisBNB).","parameters":[{"name":"route","in":"query","schema":{"type":"string","enum":["auto","bundler3","native","composer","proxy"]},"description":"Which assembly to use, for lenders that have more than one. **Morpho Blue only** today.\n\n- *(omitted)* — `auto`: prefer the protocol’s own tooling and fall back to the composer\n  for anything it cannot express (a cross-market pair, a margin paid in a foreign asset,\n  an `EXACT_OUTPUT` close, a chain with no bundler3 or no swap adapter).\n- `bundler3` / `native` — force Morpho’s bundler3. A request it cannot serve becomes an\n  ERROR instead of silently falling back, which is what makes the route testable.\n- `composer` / `proxy` — force the 1delta composer.\n\nThe default matters: the bundler3 route asks for `setAuthorization(GeneralAdapter1)`,\nMorpho’s own audited adapter, where the composer route asks for a standing grant on ours."},{"name":"marketUidIn","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for input side (`lender:chainId:address`).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for output side (`lender:chainId:address`).","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"slippage","in":"query","required":true,"schema":{"type":"number"},"description":"Slippage tolerance (basis points)","example":50},{"name":"account","in":"query","schema":{"type":"string"},"description":"Account address. Include to build transaction, omit for quote-only.","example":"0xYourAddress"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"tradeType","in":"query","schema":{"type":"integer","enum":[0,1]},"description":"Trade type (0=EXACT_INPUT, 1=EXACT_OUTPUT)"},{"name":"irModeOut","in":"query","schema":{"type":"integer","enum":[0,1,2]},"description":"Interest rate mode for debt"},{"name":"usePendleMintRedeem","in":"query","schema":{"type":"boolean"},"description":"Use Pendle mint/redeem"},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Repay full debt"},{"name":"loanId","in":"query","schema":{"type":"string"},"description":"**Lista DAO fixed-term (brokered) debt markets only.** Identifies which loan the close repays\non the loop's repay leg: the loan's `loanId` (broker posId) from the user-positions response,\nor the `type(uint128).max` sentinel (`340282366920938463463374607431768211455`) for the\nflexible/dynamic position. Required when `marketUidOut` is a brokered market; ignored otherwise.","example":"450"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Account ID"}],"responses":{"200":{"description":"Quote or full build response","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/MarginQuoteResponse"},{"$ref":"#/components/schemas/MarginBuildResponse"}],"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Actions › Loop"],"summary":"Close position (simulate)","description":"Close a leveraged position and simulate post-trade state. Same parameters as GET. Optionally send a JSON body with current portfolio state (`balanceData`, `aprData`, `positions`) to receive projected post-trade metrics in the `simulation` field — if omitted, the API fetches balances on-chain automatically. Use the data returned by the user-positions endpoint directly — always include `positions` for accurate health-factor and borrow-capacity projections.","parameters":[{"name":"route","in":"query","schema":{"type":"string","enum":["auto","bundler3","native","composer","proxy"]},"description":"Which assembly to use, for lenders that have more than one. **Morpho Blue only** today.\n\n- *(omitted)* — `auto`: prefer the protocol’s own tooling and fall back to the composer\n  for anything it cannot express (a cross-market pair, a margin paid in a foreign asset,\n  an `EXACT_OUTPUT` close, a chain with no bundler3 or no swap adapter).\n- `bundler3` / `native` — force Morpho’s bundler3. A request it cannot serve becomes an\n  ERROR instead of silently falling back, which is what makes the route testable.\n- `composer` / `proxy` — force the 1delta composer.\n\nThe default matters: the bundler3 route asks for `setAuthorization(GeneralAdapter1)`,\nMorpho’s own audited adapter, where the composer route asks for a standing grant on ours."},{"name":"marketUidIn","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for input side (`lender:chainId:address`).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for output side (`lender:chainId:address`).","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"slippage","in":"query","required":true,"schema":{"type":"number"},"description":"Slippage tolerance (basis points)","example":50},{"name":"account","in":"query","schema":{"type":"string"},"description":"Account address. Include to build transaction, omit for quote-only.","example":"0xYourAddress"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"tradeType","in":"query","schema":{"type":"integer","enum":[0,1]},"description":"Trade type (0=EXACT_INPUT, 1=EXACT_OUTPUT)"},{"name":"irModeOut","in":"query","schema":{"type":"integer","enum":[0,1,2]},"description":"Interest rate mode for debt"},{"name":"usePendleMintRedeem","in":"query","schema":{"type":"boolean"},"description":"Use Pendle mint/redeem"},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Repay full debt"},{"name":"loanId","in":"query","schema":{"type":"string"},"description":"**Lista DAO fixed-term (brokered) debt markets only.** Identifies which loan the close repays\non the loop's repay leg: the loan's `loanId` (broker posId) from the user-positions response,\nor the `type(uint128).max` sentinel (`340282366920938463463374607431768211455`) for the\nflexible/dynamic position. Required when `marketUidOut` is a brokered market; ignored otherwise.","example":"450"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Account ID"}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. If omitted, the API fetches balances on-chain automatically (slower, no `simulation` in response). When provided, pass `balanceData`, `aprData`, and `positions` directly from the matching sub-account in the `/v1/data/lending/user-positions` response — see the `SimulationBody` schema for a step-by-step example.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationBody"}}}},"responses":{"200":{"description":"Quote or full build with simulation","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/MarginSimulationQuoteResponse"},{"$ref":"#/components/schemas/MarginSimulationBuildResponse"}],"description":"Quotes and simulation results"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/loop/refinance":{"get":{"tags":["Actions › Loop"],"summary":"Refinance / roll-over (move debt to a fixed term)","description":"Move a user's fixed-term debt into a fresh term — \"refinance\" / \"roll-over\".\n\nServes **two** lenders, each with its own mechanism (the response shape is the same: a transaction plus its permissions):\n\n- **Lista DAO brokered markets** — a flash-loan-backed 1delta composer bundle (repay the source loan, open the new fixed loan).\n- **Exactly** — the protocol's OWN periphery (`DebtManager` on Optimism, `DebtRoller` on Base): ONE direct transaction that flash-loans from Balancer, repays the source position and re-borrows into the target. Because it acts on `msg.sender`'s own position it must be sent by the borrower, and it needs a Market **borrow allowance** (returned as a permission). Maps `fromLoanId` → source maturity and `termId` → target maturity, with `termId=0` meaning \"roll into the variable/floating position\" and an absent `fromLoanId` meaning \"roll FROM the floating position\". Base ships only the fixed→fixed direction. The response adds an `exactly` block (`direction`, `percentage`, `rolledAssets`, `projectedFaceValue`).\n\n\n- **Refinance** (default, omit `fromLoanId`): roll the **dynamic/float** position into a fixed term — e.g. an expired fixed loan the keeper rolled into the dynamic bracket. Lista's own `refinanceMaturedFixedPositions` (matured fixed → dynamic) is keeper-only; this is the user-driven inverse.\n- **Roll-over** (`fromLoanId` = a fixed loan's posId): roll one **fixed** loan into a different term (rate/duration). Rolling a not-yet-matured fixed loan repays it early, so `amount` must also cover its accrued interest + early-repayment penalty.\n\nSame collateral, same principal, different rate bracket.\n\n**Flash-loan backed** (robust at any LTV). The flash comes free from the market's own Moolah singleton, and the new fixed loan repays it. The returned transaction is a single composer bundle:\n```\nflashLoan(loanToken, amount) from Moolah:\n  approve loanToken → Moolah            # so Moolah can pull the flash repayment\n  broker.repay(amount, DYNAMIC, user)   # repay the float position with the flashed funds\n  broker.borrow(amount, termId, user, receiver=composer)  # open the new fixed loan; repays the flash\n→ sweep residual loanToken back to user\n```\nBecause the dynamic is repaid **before** the fixed borrow, the borrow's health check sees the original (net) debt — no 2× peak — so it works regardless of LTV.\n\nNet debt is unchanged. A `setAuthorization(composer)` permission (on Moolah) is returned **only when the composer isn't already authorized** — the broker's on-behalf borrow requires it, but `setAuthorization` reverts `AlreadySet()` if re-applied, so the server reads `isAuthorized(user, composer)` on-chain and omits the permission when it's already set. `actions.permissions` is empty in that case.\n\n**Notes:**\n- Single-market, debt-side only — pass the **loan-token** `marketUid` of a brokered market.\n- **Full close (default — omit `amount`):** the server sizes the flash to the source loan's borrow balance + a small (0.05%) margin, clearing it to zero (the broker refunds the margin, swept back). The balance is the **max** of (a) the `borrowBalance` you POST in the body and (b) a fresh on-chain read — robust whether or not the worker's live RPC is current. Avoids the `REMAIN_BORROW_TOO_LOW` revert from a stale client snapshot `amount`. **Prefer POST** with `borrowBalance` (+ `earlyRepayPenalty` for a not-yet-matured fixed source) from `/v1/data/lending/user-positions`; a plain GET also works (on-chain read only). Never make the user hand-pick an over-funded amount.\n- **Partial re-fix:** pass an explicit `amount` (leave `isAll` unset) < the source outstanding. Both the new fixed (`amount`) and the remaining source must clear `minLoan`.","requestBody":{"required":false,"description":"Optional source-loan position data for sizing a full close (sent as POST). When provided, the server sizes the flash to the **max** of this `borrowBalance` (+ `earlyRepayPenalty`) and a fresh on-chain read, plus a small margin — so it never depends solely on the worker's live RPC. Pass the source loan's current debt from `/v1/data/lending/user-positions`. Ignored for a partial re-fix (explicit `amount`, `isAll` unset).","content":{"application/json":{"schema":{"type":"object","properties":{"borrowBalance":{"type":"string","description":"Source loan's current borrow balance (loan-token wei).","example":"100030000000000000"},"earlyRepayPenalty":{"type":"string","description":"Source loan's early-repay penalty (loan-token wei) — fixed, not-yet-matured source only.","example":"0"}}}}}},"parameters":[{"name":"marketUid","in":"query","required":true,"schema":{"type":"string"},"description":"Loan-token market identifier of a Lista brokered market (`lender:chainId:address`).","example":"LISTA_DAO_226935…3cac:56:0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"},{"name":"amount","in":"query","schema":{"type":"string"},"description":"Flash / repay / new-fixed amount (loan-token wei). **Required only for a partial re-fix** (provide `amount`, leave `isAll` unset). For a full close, omit it — the server sizes the flash itself; if present alongside a full close it is used only as a floor hint.","example":"50000000000000000"},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Force a full close even when `amount` is present. A full close is also the DEFAULT whenever `amount` is omitted. The server sizes the flash to the source loan's borrow balance — the max of the POST-body `borrowBalance` (+ `earlyRepayPenalty`) and a fresh on-chain read (dynamic = `getUserTotalDebt` minus each fixed position's current debt; fixed = that position's current debt + early-repay penalty) — plus a small (0.05%) margin. The repay clears the source to zero and the broker refunds the margin (swept back), so there is no sub-`minLoan` dust and no `REMAIN_BORROW_TOO_LOW`. Set `amount` (and leave this unset) only for a deliberate partial re-fix.","example":"true"},{"name":"termId","in":"query","required":true,"schema":{"type":"integer"},"description":"Fixed term to move the debt into, from the market's `terms[]` rate card (`MarketTerm.termId`).","example":1},{"name":"fromLoanId","in":"query","schema":{"type":"string"},"description":"Source loan to roll FROM: a fixed loan's `loanId` (posId) to roll one fixed loan into another term, or omit (defaults to `type(uint128).max`) to refinance the dynamic/float position.","example":"450"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"The borrower (and tx sender). Both the repaid source loan and the new fixed loan are this account.","example":"0xYourAddress"}],"responses":{"200":{"description":"Flash-loan composer transaction (repay source loan + borrow new fixed term) and the borrow-authorization permission","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"$ref":"#/components/schemas/LendingProxyResponse","description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/loop/collateral-swap":{"get":{"tags":["Actions › Loop"],"summary":"Collateral swap","description":"Swap collateral assets within a lending position. Identify the sell-side collateral market with `marketUidIn` and buy-side with `marketUidOut`. Omit `account` for quote-only (returns `data.quotes` with price deltas). Include `account` to build full transaction calldata (populates `actions` with `alternatives`, `transactions`, and `permissions`).\n\n**Response `actions` fields (when `account` is provided):**\n- `alternatives`: DEX aggregator swap transactions sorted best-output-first. Pick one to execute.\n- `transactions`: Collateral enable (`enterMarkets`) for Compound V2 lenders. Empty otherwise.\n- `permissions`: Lender withdrawal delegations (targeting the lending protocol contract). Only missing delegations are returned. Execute these first.","parameters":[{"name":"route","in":"query","schema":{"type":"string","enum":["auto","bundler3","native","composer","proxy"]},"description":"Which assembly to use, for lenders that have more than one. **Morpho Blue only** today.\n\n- *(omitted)* — `auto`: prefer the protocol’s own tooling and fall back to the composer\n  for anything it cannot express (a cross-market pair, a margin paid in a foreign asset,\n  an `EXACT_OUTPUT` close, a chain with no bundler3 or no swap adapter).\n- `bundler3` / `native` — force Morpho’s bundler3. A request it cannot serve becomes an\n  ERROR instead of silently falling back, which is what makes the route testable.\n- `composer` / `proxy` — force the 1delta composer.\n\nThe default matters: the bundler3 route asks for `setAuthorization(GeneralAdapter1)`,\nMorpho’s own audited adapter, where the composer route asks for a standing grant on ours."},{"name":"marketUidIn","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for input side (`lender:chainId:address`).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for output side (`lender:chainId:address`).","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"slippage","in":"query","required":true,"schema":{"type":"number"},"description":"Slippage tolerance (basis points)","example":50},{"name":"account","in":"query","schema":{"type":"string"},"description":"Account address. Include to build transaction, omit for quote-only.","example":"0xYourAddress"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"tradeType","in":"query","schema":{"type":"integer","enum":[0,1]},"description":"Trade type"},{"name":"usePendleMintRedeem","in":"query","schema":{"type":"boolean"},"description":"Use Pendle mint/redeem"},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Swap full collateral balance"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Account ID"}],"responses":{"200":{"description":"Quote or full build response","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/MarginQuoteResponse"},{"$ref":"#/components/schemas/MarginBuildResponse"}],"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Actions › Loop"],"summary":"Collateral swap (simulate)","description":"Swap collateral assets and simulate post-trade state. Same parameters as GET. Optionally send a JSON body with current portfolio state (`balanceData`, `aprData`, `positions`) to receive projected post-trade metrics in the `simulation` field — if omitted, the API fetches balances on-chain automatically. Use the data returned by the user-positions endpoint directly — always include `positions` for accurate health-factor and borrow-capacity projections.\n\nFor `isAll` trades, include `depositBalanceIn` (raw underlying balance string) in the body so the withdrawal approval is sized to the actual position. This field is accepted alongside or independently of the simulation fields.","parameters":[{"name":"route","in":"query","schema":{"type":"string","enum":["auto","bundler3","native","composer","proxy"]},"description":"Which assembly to use, for lenders that have more than one. **Morpho Blue only** today.\n\n- *(omitted)* — `auto`: prefer the protocol’s own tooling and fall back to the composer\n  for anything it cannot express (a cross-market pair, a margin paid in a foreign asset,\n  an `EXACT_OUTPUT` close, a chain with no bundler3 or no swap adapter).\n- `bundler3` / `native` — force Morpho’s bundler3. A request it cannot serve becomes an\n  ERROR instead of silently falling back, which is what makes the route testable.\n- `composer` / `proxy` — force the 1delta composer.\n\nThe default matters: the bundler3 route asks for `setAuthorization(GeneralAdapter1)`,\nMorpho’s own audited adapter, where the composer route asks for a standing grant on ours."},{"name":"marketUidIn","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for input side (`lender:chainId:address`).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for output side (`lender:chainId:address`).","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"slippage","in":"query","required":true,"schema":{"type":"number"},"description":"Slippage tolerance (basis points)","example":50},{"name":"account","in":"query","schema":{"type":"string"},"description":"Account address. Include to build transaction, omit for quote-only.","example":"0xYourAddress"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"tradeType","in":"query","schema":{"type":"integer","enum":[0,1]},"description":"Trade type"},{"name":"usePendleMintRedeem","in":"query","schema":{"type":"boolean"},"description":"Use Pendle mint/redeem"},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Swap full collateral balance"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Account ID"}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. Accepts standard SimulationBody fields and/or `depositBalanceIn` (raw balance string) for accurate isAll withdrawal approvals.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/SimulationBody"},{"type":"object","properties":{"depositBalanceIn":{"type":"string","description":"Raw deposit balance of the input collateral asset in underlying units. Used for isAll to size the withdrawal approval correctly.","example":"1000000000000000000"}}}]}}}},"responses":{"200":{"description":"Quote or full build with simulation","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/MarginSimulationQuoteResponse"},{"$ref":"#/components/schemas/MarginSimulationBuildResponse"}],"description":"Quotes and simulation results"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/loop/debt-swap":{"get":{"tags":["Actions › Loop"],"summary":"Debt swap","description":"Swap debt between different borrow positions. Identify the repay-side debt market with `marketUidIn` and borrow-side with `marketUidOut`. Omit `account` for quote-only (returns `data.quotes` with price deltas). Include `account` to build full transaction calldata (populates `actions` with `alternatives`, `transactions`, and `permissions`).\n\n**Response `actions` fields (when `account` is provided):**\n- `alternatives`: DEX aggregator swap transactions sorted best-output-first. Pick one to execute.\n- `transactions`: Empty for debt-swap operations (no setup needed).\n- `permissions`: Lender borrow and withdrawal delegations (targeting the lending protocol contract). Only missing delegations are returned. Execute these first.","parameters":[{"name":"route","in":"query","schema":{"type":"string","enum":["auto","bundler3","native","composer","proxy"]},"description":"Which assembly to use, for lenders that have more than one. **Morpho Blue only** today.\n\n- *(omitted)* — `auto`: prefer the protocol’s own tooling and fall back to the composer\n  for anything it cannot express (a cross-market pair, a margin paid in a foreign asset,\n  an `EXACT_OUTPUT` close, a chain with no bundler3 or no swap adapter).\n- `bundler3` / `native` — force Morpho’s bundler3. A request it cannot serve becomes an\n  ERROR instead of silently falling back, which is what makes the route testable.\n- `composer` / `proxy` — force the 1delta composer.\n\nThe default matters: the bundler3 route asks for `setAuthorization(GeneralAdapter1)`,\nMorpho’s own audited adapter, where the composer route asks for a standing grant on ours."},{"name":"marketUidIn","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for input side (`lender:chainId:address`).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for output side (`lender:chainId:address`).","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"slippage","in":"query","required":true,"schema":{"type":"number"},"description":"Slippage tolerance (basis points)","example":50},{"name":"account","in":"query","schema":{"type":"string"},"description":"Account address. Include to build transaction, omit for quote-only.","example":"0xYourAddress"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000"},{"name":"tradeType","in":"query","schema":{"type":"integer","enum":[0,1]},"description":"Trade type"},{"name":"irModeIn","in":"query","schema":{"type":"integer","enum":[0,1,2]},"description":"Interest rate mode for repaid debt"},{"name":"irModeOut","in":"query","schema":{"type":"integer","enum":[0,1,2]},"description":"Interest rate mode for new debt"},{"name":"usePendleMintRedeem","in":"query","schema":{"type":"boolean"},"description":"Use Pendle mint/redeem"},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Repay full debt"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Account ID"}],"responses":{"200":{"description":"Quote or full build response","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/MarginQuoteResponse"},{"$ref":"#/components/schemas/MarginBuildResponse"}],"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Actions › Loop"],"summary":"Debt swap (simulate)","description":"Swap debt between borrow positions and simulate post-trade state. Same parameters as GET. Optionally send a JSON body with current portfolio state (`balanceData`, `aprData`, `positions`) to receive projected post-trade metrics in the `simulation` field — if omitted, the API fetches balances on-chain automatically. Use the data returned by the user-positions endpoint directly — always include `positions` for accurate health-factor and borrow-capacity projections.","parameters":[{"name":"route","in":"query","schema":{"type":"string","enum":["auto","bundler3","native","composer","proxy"]},"description":"Which assembly to use, for lenders that have more than one. **Morpho Blue only** today.\n\n- *(omitted)* — `auto`: prefer the protocol’s own tooling and fall back to the composer\n  for anything it cannot express (a cross-market pair, a margin paid in a foreign asset,\n  an `EXACT_OUTPUT` close, a chain with no bundler3 or no swap adapter).\n- `bundler3` / `native` — force Morpho’s bundler3. A request it cannot serve becomes an\n  ERROR instead of silently falling back, which is what makes the route testable.\n- `composer` / `proxy` — force the 1delta composer.\n\nThe default matters: the bundler3 route asks for `setAuthorization(GeneralAdapter1)`,\nMorpho’s own audited adapter, where the composer route asks for a standing grant on ours."},{"name":"marketUidIn","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for input side (`lender:chainId:address`).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidOut","in":"query","required":true,"schema":{"type":"string"},"description":"Market identifier for output side (`lender:chainId:address`).","example":"AAVE_V3:1:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"},{"name":"slippage","in":"query","required":true,"schema":{"type":"number"},"description":"Slippage tolerance (basis points)","example":50},{"name":"account","in":"query","schema":{"type":"string"},"description":"Account address. Include to build transaction, omit for quote-only.","example":"0xYourAddress"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000"},{"name":"tradeType","in":"query","schema":{"type":"integer","enum":[0,1]},"description":"Trade type"},{"name":"irModeIn","in":"query","schema":{"type":"integer","enum":[0,1,2]},"description":"Interest rate mode for repaid debt"},{"name":"irModeOut","in":"query","schema":{"type":"integer","enum":[0,1,2]},"description":"Interest rate mode for new debt"},{"name":"usePendleMintRedeem","in":"query","schema":{"type":"boolean"},"description":"Use Pendle mint/redeem"},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Repay full debt"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Account ID"}],"requestBody":{"required":false,"description":"Optional current portfolio state for post-trade simulation. If omitted, the API fetches balances on-chain automatically (slower, no `simulation` in response). When provided, pass `balanceData`, `aprData`, and `positions` directly from the matching sub-account in the `/v1/data/lending/user-positions` response — see the `SimulationBody` schema for a step-by-step example.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationBody"}}}},"responses":{"200":{"description":"Quote or full build with simulation","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/MarginSimulationQuoteResponse"},{"$ref":"#/components/schemas/MarginSimulationBuildResponse"}],"description":"Quotes and simulation results"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/loop/migrate":{"get":{"tags":["Actions › Loop"],"summary":"Migrate position (cross-lender)","operationId":"loop-migrate","description":"Move a whole debt + collateral position from one lender to another in a single flash-loan transaction, optionally converting ONE leg (collateral OR debt) via an aggregator swap.\n\nFlow (no conversion): flash the debt → repay the source → withdraw the source collateral → deposit it to the target → borrow the debt from the target → repay the flash. Same-lender / different-market moves (e.g. Morpho market A→B, Aave V4 reserve→reserve) are supported.\n\n**Supported sources & targets:** Aave V2/V3, Aave V4, Compound V3, Morpho Blue / Lista, Fluid (an existing position via NFT-custody, OR a brand-new position — see below), Gearbox V3 (existing credit account as source; a fresh credit account is opened in-callback as target), Euler V2 (accountId 0; collateral-share transfer through the call forwarder). **Not supported:** Dolomite (either side), Euler as a TARGET, non-Venus Compound V2 as a target, both legs converting at once, Fluid↔Fluid.\n\n> **Fluid target — existing vs fresh:** pass the position's NFT id in `accountId` to migrate INTO an existing Fluid position (delivered via the VaultFactory NFT-custody flow). Omit it (or pass 0) to open a **brand-new** Fluid position: the migrate emits a single dual-axis Fluid `operate` that supplies the withdrawn collateral, borrows the debt, and mints the new position NFT straight to you — no NFT custody, no target-side consent, and the transaction targets the composer (`deltaCompose`) normally.\n\n> **Native ↔ wrapped-native (wrap conversion):** a native debt — e.g. a Venus `vBNB` borrow — migrates into a target lender's **wrapped-native** debt market (WBNB). On-behalf borrowing cannot be delegated for the native asset, so the target MUST be the wrapped form; a native TARGET debt is rejected. The builder flashes the wrapped form, unwraps it to repay the native source, and borrows the wrapped form on the target — no aggregator needed. Compound V2 / Venus can be a migration **source** but never a **target** (no borrow delegation). The **collateral** leg wraps BOTH ways — a native source collateral (e.g. Fluid's ETH slot) is wrapped to WETH before the target deposit, and a wrapped source collateral is unwrapped for a native-collateral target — again with no aggregator. Native collateral is only reachable as such a wrap pair; native → an unrelated ERC20 target is rejected.\n\n> **Lista DAO markets:** two per-market extras decide the shape. A **brokered** market (non-zero `broker`) routes its debt leg through the fixed-term broker — pass `loanId` when it is the SOURCE (which loan to repay) and `termId` when it is the TARGET (which term to open); the target has no default because the broker's flexible borrow has no on-behalf variant. A market with a **`collateralProvider`** (e.g. the slisBNB markets) routes its collateral leg through that provider, which exposes no `position()` getter — so a provider-gated SOURCE cannot use the withdraw-all sentinel and is sized from your live collateral balance instead (read server-side; pass `collateralAmount` to override).\n\n> **Asset conversion (swap leg):** ONE leg may change asset via an aggregator swap — either the collateral (`marketUidTargetCollateral` uses a different underlying) or the debt (`marketUidTargetDebt`), not both. The route fetches the aggregator quote server-side and bakes the trade into the flash callback. A **collateral** swap is EXACT_INPUT of the withdrawn collateral → provide a concrete `collateralAmount` (or `collateralAmountHint`) so the swap input lines up. A **debt** swap is EXACT_OUTPUT (buy exactly the source debt to repay, selling the flashed target debt). Tune the swap tolerance with `slippage`. **Euler as a source** still needs an off-chain eVault share-balance read via the `prepareMigrate` SDK.\n\nOmit `account` for quote-only (returns `data.quotes` with price deltas). Include `account` to build full transaction calldata (populates `actions` with `alternatives`, `transactions`, and `permissions`).\n\n**Setup transactions (`actions.permissions`):** returned only for the consents actually missing, to be executed BEFORE the migrate. These vary by lender pair — e.g. the source collateral withdrawal approval; Aave V4 Giver/Taker/Config Position-Manager authorizations + per-reserve borrow allowance + collateral-enable grant; Morpho `setAuthorization`; a Gearbox V3 SOURCE's `setBotPermissions` grant on the credit account; Euler collateral eVault share approval to the composer. Fluid legs need no separate permission (NFT custody — or, for a fresh open, the composer opening on your behalf — IS the authorization).\n\n**Delivery:** the migrate transaction targets the composer (`deltaCompose`) normally, OR the Fluid `VaultFactory.safeTransferFrom` when a Fluid leg requires NFT custody — `actions.transactions[0].to` reflects this.\n\n**`data.result` (resulting position):** a summary for the UI — `from`/`to` lenders with their collateral + debt assets (address/symbol/decimals/logo), the new position amounts (`to.collateral.amount`/`amountUsd`, `to.debt.amount`/`amountUsd`), `netUsd`, `leverage`, `apr: { deposit, borrow, net }` and `healthFactor`. The rates, liquidation threshold, decimals and prices behind those are **resolved server-side** from the target markets — the corresponding query params are overrides only. USD/symbol fields are best-effort (omitted when price/token metadata is unavailable). `to.collateral.amount` is present when the withdraw was sized server-side (Aave source) or passed explicitly.\n\n**Finding a target:** [`GET /v1/actions/loop/migrate/targets`](#operation/loop-migrate-targets) returns the ranked destinations this endpoint accepts for a given position — support matrix, native ⇄ wrapped equivalence, un-openable fixed-term books and borrow liquidity all applied server-side.","parameters":[{"name":"route","in":"query","schema":{"type":"string","enum":["auto","bundler3","native","composer","proxy"]},"description":"Which assembly to use, for lenders that have more than one. **Morpho Blue only** today.\n\n- *(omitted)* — `auto`: prefer the protocol’s own tooling and fall back to the composer\n  for anything it cannot express (a cross-market pair, a margin paid in a foreign asset,\n  an `EXACT_OUTPUT` close, a chain with no bundler3 or no swap adapter).\n- `bundler3` / `native` — force Morpho’s bundler3. A request it cannot serve becomes an\n  ERROR instead of silently falling back, which is what makes the route testable.\n- `composer` / `proxy` — force the 1delta composer.\n\nThe default matters: the bundler3 route asks for `setAuthorization(GeneralAdapter1)`,\nMorpho’s own audited adapter, where the composer route asks for a standing grant on ours."},{"name":"marketUidSourceCollateral","in":"query","required":true,"schema":{"type":"string"},"description":"Source collateral market (`lender:chainId:address`). The position being moved FROM.","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidSourceDebt","in":"query","required":true,"schema":{"type":"string"},"description":"Source debt market (`lender:chainId:address`).","example":"AAVE_V3:1:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},{"name":"marketUidTargetCollateral","in":"query","required":true,"schema":{"type":"string"},"description":"Target collateral market (`lender:chainId:address`). The position being moved TO. May be a different lender and/or a different asset (collateral conversion).","example":"AAVE_V4_94E7A5DCBE816E498B89AB752661904E2F56C485:1:0"},{"name":"marketUidTargetDebt","in":"query","required":true,"schema":{"type":"string"},"description":"Target debt market (`lender:chainId:address`). May be a different lender and/or a different asset (debt conversion).","example":"AAVE_V4_94E7A5DCBE816E498B89AB752661904E2F56C485:1:8"},{"name":"debtAmount","in":"query","required":true,"schema":{"type":"string"},"description":"Live debt to migrate, in the debt asset’s wei. For a debt conversion this is the NEW-debt (flash/borrow) amount instead. The flash is sized a small buffer above this so the source repay clears in full (a residual would make the full collateral withdrawal revert).","example":"1000000000"},{"name":"account","in":"query","schema":{"type":"string"},"description":"Account address. Include to build the transaction + permission setup; omit for a quote-only response.","example":"0xYourAddress"},{"name":"isMaxIn","in":"query","schema":{"type":"boolean"},"description":"Withdraw the full collateral balance from the source (default true for a same-asset move). Set false + `collateralAmount` for an exact-amount conversion so the swap input lines up."},{"name":"collateralAmount","in":"query","schema":{"type":"string"},"description":"Exact source collateral to withdraw, in wei. Required (with `isMaxIn=false`) for a collateral conversion so the withdrawn amount matches the swap input.","example":"1000000000000000000"},{"name":"accountId","in":"query","schema":{"type":"string"},"description":"Per-position id where the lender needs one: an existing Fluid NFT id (omit or 0 opens a BRAND-NEW Fluid position), Euler sub-account index (must be 0 — sub-accounts unsupported). Defaults to 0."},{"name":"loanId","in":"query","schema":{"type":"string"},"description":"Lista fixed-term broker SOURCE only: the loan `posId` to repay, or `type(uint128).max` for the flex/dynamic position. Required when the source debt market is brokered; ignored otherwise. The target borrow is keyed by term, not loanId.","example":"340282366920938463463374607431768211455"},{"name":"termId","in":"query","schema":{"type":"integer"},"description":"Lista fixed-term broker TARGET only: which term the migrated debt opens at. REQUIRED when the target debt market is brokered — the broker exposes no on-behalf flexible borrow, so there is no default; ignored otherwise. Mirrors the `termId` on /v1/actions/loop/open.","example":1},{"name":"irModeFrom","in":"query","schema":{"type":"integer","enum":[0,1,2]},"description":"Aave interest mode of the source debt (repay). 2 = variable."},{"name":"irModeTo","in":"query","schema":{"type":"integer","enum":[0,1,2]},"description":"Aave interest mode of the target debt (borrow). 2 = variable."},{"name":"slippage","in":"query","schema":{"type":"string"},"description":"Swap-leg slippage tolerance as a FRACTION (`0.005` = 0.5%). Only used\nwhen a leg is converted via an aggregator swap; ignored for same-asset /\nwrap moves. Defaults to 0.5%.\n\n⚠ **This endpoint is the exception.** Every other `slippage` in this API\nis in BASIS POINTS (`50` = 0.5%) — migrate takes a fraction, which is\n100× smaller for the same tolerance. Sending `50` here would ask for\n5000% slippage.","example":"0.005"},{"name":"eModeTo","in":"query","schema":{"type":"string"},"description":"Risk-config category to read the TARGET collateral's liquidation threshold from (Aave-style e-modes publish one config per category). Defaults to `0` (no e-mode). Display only — it does not change the built transaction.","example":"1"},{"name":"collateralDecimals","in":"query","schema":{"type":"integer"},"description":"**Override.** Target collateral decimals. Resolved server-side; pass only to overrule the published metadata (e.g. a market whose token-list decimals are known-wrong, which would mis-scale the `data.result` USD values). Never affects the on-chain amounts.","example":"8"},{"name":"debtDecimals","in":"query","schema":{"type":"integer"},"description":"**Override.** TARGET debt decimals — the swap target’s when the debt converts, else the source’s. Resolved server-side; see `collateralDecimals`.","example":"6"},{"name":"sourceDebtDecimals","in":"query","schema":{"type":"integer"},"description":"**Override.** SOURCE debt decimals, for the `from`-leg display when the debt is converted. Resolved server-side; defaults to `debtDecimals`.","example":"6"},{"name":"depositApr","in":"query","schema":{"type":"string"},"description":"**Override.** Target collateral deposit APR as a FRACTION (0.05 = 5%), intrinsic yield folded in. Resolved server-side and reported as `data.result.apr.deposit`.","example":"0.031"},{"name":"borrowApr","in":"query","schema":{"type":"string"},"description":"**Override.** Target debt borrow APR as a FRACTION (0.05 = 5%), intrinsic yield folded in — for a fixed-term target this is the term rate, not the 0% variable rate. Resolved server-side.","example":"0.058"},{"name":"liqThreshold","in":"query","schema":{"type":"string"},"description":"**Override.** Target collateral liquidation threshold as a FRACTION (0.85 = 85%), driving `data.result.healthFactor` (= collateralUsd · liqThreshold / debtUsd; < 1 ⇒ liquidatable). Resolved server-side from the target market’s risk config — see `eModeTo`.","example":"0.83"},{"name":"collateralPriceUsd","in":"query","schema":{"type":"string"},"description":"**Override.** USD price of the TARGET collateral for the display conversion. Resolved server-side, preferring the market’s own ORACLE price (what the lender liquidates against).","example":"1917.56"},{"name":"debtPriceUsd","in":"query","schema":{"type":"string"},"description":"**Override.** USD price of the TARGET debt for the display conversion. Resolved server-side; see `collateralPriceUsd`.","example":"0.9998"}],"responses":{"200":{"description":"Quote or full build response","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/MarginQuoteResponse"},{"$ref":"#/components/schemas/MarginBuildResponse"}],"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/loop/migrate/targets":{"get":{"tags":["Actions › Loop"],"summary":"Migration targets for a position","operationId":"loop-migrate-targets","description":"Which markets can this position migrate to? The discovery companion to [`/v1/actions/loop/migrate`](#operation/loop-migrate) — it returns the ranked set of destinations that endpoint would actually accept, so a client never encodes protocol knowledge to build that list.\n\nGive it the two marketUids of an existing position (and, ideally, its size) and it does everything a caller would otherwise hand-roll:\n\n- **Native ⇄ wrapped-native equivalence.** Some lenders key a market to the native asset (Fluid ETH vaults), others to the wrapped ERC20 (Aave WETH); a migrate bridges them by wrapping. Selection is by shared `assetGroup`, so both forms surface in BOTH directions on every chain — no per-chain WETH table anywhere in the client.\n- **The migrate support matrix.** Lenders that cannot be a target are dropped (Dolomite, Euler, Curvance, LlamaLend and non-Venus Compound V2 forks today). This is the same code path `/migrate` validates with, so the list cannot drift out of sync with what builds.\n- **The native-debt rule.** On-behalf borrowing cannot be delegated for the native asset, so native-debt markets are dropped unless the move is a wrap conversion into Fluid.\n- **Un-openable fixed-term books.** An order-book lender (Morpho Midnight) reports a 0% variable rate and no terms in the pair feed whether its book is rich or empty. Each fixed-term candidate's real borrow term is resolved and the ones that cannot be opened are dropped — 10 of 12 Midnight borrow legs on Base at time of writing. Survivors get `termsShort` back-filled with the true rate + maturity.\n- **Borrow liquidity at this position's size**, applied BEFORE truncation (a client-side filter after paging silently loses real destinations to rows it then discards).\n\n**Rows** come back in the [`/v1/data/lending/pairs/optimize`](#operation/lending-pairs-optimize) shape, so an existing pair normaliser keeps working, plus a `migrate` block per row:\n\n```jsonc\n\"migrate\": {\n  \"healthFactor\": 1.87,          // collateralUsd · liquidationThreshold / debtUsd on THIS target\n  \"netApr\": 0.041,               // equity-weighted earn − pay for THIS position (not max-leverage aprTotal)\n  \"depositApr\": 0.031,           // effective rates behind netApr (intrinsic yield folded in)\n  \"borrowApr\": 0.058,\n  \"borrowLiquidityUsd\": 6470141,\n  \"sufficientLiquidity\": true,\n  \"maturity\": 1787929200,        // fixed-term targets only\n  \"termHeadline\": \"Fixed 4.01% until 28 Aug 2026\"\n}\n```\n\nThe envelope also carries `source` (the resolved position with its USD values), `hiddenForLiquidity`, and `excluded` — one entry per distinct drop reason, so a UI can explain a short list instead of silently showing one.\n\n> **Native-asset convention.** Every 1delta data payload spells the native asset as the ZERO ADDRESS. `0xEeee…EEeE` is an encoding-layer sentinel: it is accepted on action inputs and normalised away, but it is never served and never matches a data filter. A client filtering markets by `0xEeee…` silently gets no rows.","parameters":[{"name":"marketUidSourceCollateral","in":"query","required":true,"schema":{"type":"string"},"description":"Collateral market of the position being moved (`lender:chainId:address`).","example":"AAVE_V3:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"marketUidSourceDebt","in":"query","required":true,"schema":{"type":"string"},"description":"Debt market of the position being moved (`lender:chainId:address`). Must be on the same chain as the collateral.","example":"AAVE_V3:1:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},{"name":"debtAmount","in":"query","schema":{"type":"string"},"description":"Live debt of the position, in the debt asset’s wei. Supply it to filter out targets that cannot fund the whole borrow, to price each row AT this notional, and to get a resulting health factor per target.","example":"1000000000"},{"name":"collateralAmount","in":"query","schema":{"type":"string"},"description":"Live collateral of the position, in the collateral asset’s wei. Needed (with `debtAmount`) for the per-target health factor.","example":"1000000000000000000"},{"name":"convertLeg","in":"query","schema":{"type":"string","enum":["collateral","debt"]},"description":"Ask for targets that CONVERT one leg via an aggregator swap. With `convertTo` omitted the response carries `convertibleAssets` — the assets that leg can convert into while still pairing with the fixed leg — so a picker can be populated without knowing which pairings exist."},{"name":"convertTo","in":"query","schema":{"type":"string"},"description":"Target asset address for the converted leg. Pins that leg to exactly this asset; the fixed leg keeps the source asset (native ⇄ wrapped-native included).","example":"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"},{"name":"count","in":"query","schema":{"type":"integer"},"description":"Maximum ranked targets to return (default 50)."},{"name":"includeIlliquid","in":"query","schema":{"type":"boolean"},"description":"Keep targets whose borrow liquidity cannot fund `debtAmount` (they sort last and carry `migrate.sufficientLiquidity: false`). Default false — they are counted in `hiddenForLiquidity` instead."},{"name":"maxRiskScore","in":"query","schema":{"type":"integer"},"description":"Risk cap for candidate markets. Defaults to `100`, i.e. no cap: a migrate MOVES a position the caller already holds, so the pair browser’s default cap (which hides whole chains) would hide real destinations. Each row still carries its own `risk` breakdown."},{"name":"collateralPriceUsd","in":"query","schema":{"type":"string"},"description":"**Override.** USD price of the source collateral, for sizing the position. Resolved server-side."},{"name":"debtPriceUsd","in":"query","schema":{"type":"string"},"description":"**Override.** USD price of the source debt, for sizing the position. Resolved server-side."}],"responses":{"200":{"description":"Ranked migration targets for the given position","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/swap/spot":{"get":{"tags":["Actions › Swap"],"summary":"Spot swap (meta-aggregator)","description":"Execute a spot swap via the meta-aggregator. Omit `account` for quote-only. Include `account` to build full transaction calldata.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"tokenIn","in":"query","required":true,"schema":{"type":"string"},"description":"Input token address","example":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},{"name":"tokenOut","in":"query","required":true,"schema":{"type":"string"},"description":"Output token address","example":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei","example":"1000000000000000000"},{"name":"slippage","in":"query","required":true,"schema":{"type":"number"},"description":"Slippage tolerance (basis points)","example":50},{"name":"account","in":"query","schema":{"type":"string"},"description":"Account address. Include to build transaction, omit for quote-only.","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Receiver address"},{"name":"tradeType","in":"query","schema":{"type":"integer","enum":[0,1]},"description":"Trade type (0=EXACT_INPUT, 1=EXACT_OUTPUT)"},{"name":"usePendleMintRedeem","in":"query","schema":{"type":"boolean"},"description":"Use Pendle mint/redeem"}],"responses":{"200":{"description":"Spot swap quote or full build","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/SpotQuoteResponse"},{"$ref":"#/components/schemas/SpotBuildResponse"}],"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/swap/x-chain":{"get":{"tags":["Actions › Swap"],"summary":"Cross-chain swap (bridge aggregation)","description":"Quote a cross-chain swap across all supported bridge aggregators (Across, LI.FI, Squid, Stargate, Symbiosis, XY Finance, DZap, …) and build the winning transactions. Omit `account` for quote-only. Include `account` to build full transaction calldata. Each build quote carries `approvalTarget`/`approvalRequired`, and `actions.permissions` holds one ERC-20 approve per unique spender (composed routes share the 1delta composer; plain bridges use their own deposit contract) with a `spender` field — execute only the permission whose `spender` equals your chosen quote's `approvalTarget`. When `fromChainId` equals `toChainId` the request falls back to the spot meta-aggregator (same response shape with `aggregator` instead of `bridge` per quote, marked `fallback: 'spot'`).","parameters":[{"name":"fromChainId","in":"query","required":true,"schema":{"type":"string"},"description":"Source chain ID","example":"1"},{"name":"toChainId","in":"query","required":true,"schema":{"type":"string"},"description":"Destination chain ID","example":"8453"},{"name":"tokenIn","in":"query","required":true,"schema":{"type":"string"},"description":"Input token address on the source chain (zero address for native)","example":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},{"name":"tokenOut","in":"query","required":true,"schema":{"type":"string"},"description":"Output token address on the destination chain (zero address for native)","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Input amount in wei","example":"1000000000"},{"name":"slippage","in":"query","required":true,"schema":{"type":"number"},"description":"Slippage tolerance (basis points)","example":50},{"name":"account","in":"query","schema":{"type":"string"},"description":"Account address on the source chain. Include to build transactions, omit for quote-only.","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Receiver address on the destination chain (defaults to account)"},{"name":"order","in":"query","schema":{"type":"string","enum":["CHEAPEST","FASTEST"]},"description":"Route preference"},{"name":"bridges","in":"query","schema":{"type":"string"},"description":"Comma-separated bridge filter (e.g. `Across,LI.FI`). Defaults to all supported bridges."}],"responses":{"200":{"description":"Cross-chain swap quote or full build","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/XChainQuoteResponse"},{"$ref":"#/components/schemas/XChainBuildResponse"},{"$ref":"#/components/schemas/SpotQuoteResponse"},{"$ref":"#/components/schemas/SpotBuildResponse"}],"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/earn/{action}":{"get":{"tags":["Actions"],"summary":"One deposit/withdraw entry point over every earn row","operationId":"earnAction","description":"**One action entry point for every `/v1/data/earn` row.** The `earnUid` a listing row carries is the only identity needed — this route parses it, decides which existing handler owns the row (lending vs vault, and which vault family), rewrites the query into that handler's vocabulary and calls it in-process. Nothing is built here and no route is duplicated: if a market can be transacted through `/v1/actions/lending/*` or `/v1/actions/vaults/*`, it can be transacted through here.\n\n## Routing\n\n```\nAAVE_V3:1:0xa0b8…          → /v1/actions/lending/{deposit,withdraw}\nvault.savings:1:0x9d39…    → /v1/actions/vaults/{deposit,withdraw}\n                           → /v1/actions/vaults/savings   (async verbs)\nvault.lst:…      (async)   → /v1/actions/vaults/lst       (queued LST exits)\nvault.lagoon:…   (async)   → /v1/actions/vaults/withdraw  (ERC-7540 requestRedeem / claimRedeem)\nvault.gmx:…                → /v1/actions/vaults/gmx       (keeper tickets; no claim — the keeper pays out)\nvault.hypercore:…          → /v1/actions/vaults/{deposit,withdraw} with interface=hypercore\nvault.pendle:…             → traded on Pendle's AMM (slippage REQUIRED, bps)\n```\n\n## Pay-asset conversion (vault rows)\n\nA `deposit` whose `payAsset` differs from the vault's underlying is served HERE and nowhere else: the pay asset is quoted through the aggregators and the whole swap output is deposited in one composed transaction, shares to `receiver`. `slippage` (bps) becomes REQUIRED, and the built transactions come back as `alternatives[]` — one per aggregator, best output first. Only rows whose deposit is a plain synchronous ERC-4626 call qualify (that is what `acceptsPayAsset` on the row's capability advertises); paying native into a wrapped-native vault stays a plain wrap with no bound. `/v1/actions/vaults/deposit` deliberately does NOT gain this behaviour.\n\n## Verbs\n\n- `deposit` / `withdraw` — the synchronous pair. On a lending market both are same-block; on a vault, `withdraw` is only published where the exit really is synchronous.\n- `request-withdraw` / `claim` / `cancel` — the async exit cycle, **vaults only**. Asking for one on a lending market is a 400, never a silent fallback to `withdraw`.\n\n**Which verbs a row supports — and which extra params each needs — is published on the row itself as `capabilities[]`** (see `/v1/data/earn`). Render the CTA from that, not from provider knowledge: `requires` names params like `slippage` (Pendle, bps), `executionFee`/`longToken`/`shortToken` (GMX), or `validator` (delegated LSTs).\n\n## Parameter forwarding\n\nOnly `earnUid` and the verb are consumed. Everything else — `payAsset`, `receiveAsset`, `slippage`, `isShares`, `isAll`, `mode`, Yield Basis's `debt`/`minShares`, Strata's `claimToken`, Apyx's `tokenId`, an LST's `kind`, GMX's `executionFee`, an ERC-7540 sub-action (`action=claimWithdraw`), … — rides through verbatim to the owning handler, so provider-specific inputs need no support here.\n\nResponse is the standard action envelope: `permissions[]` (approvals, executed first), `transactions[]` (signed in order), `alternatives[]` where a trade offers multiple routes, plus per-venue state (e.g. `dbr` on Inverse) in `data`.","parameters":[{"name":"action","in":"path","required":true,"schema":{"type":"string","enum":["deposit","withdraw","request-withdraw","claim","cancel"]},"description":"The verb. May also be passed as `?action=` on the bare `/v1/actions/earn` path."},{"name":"earnUid","in":"query","required":true,"schema":{"type":"string"},"description":"The row identity from `/v1/data/earn`, verbatim and OPAQUE — never rebuild it from parts. Lending rows: the `marketUid` (`AAVE_V3:1:0xa0b8…`). Vault rows: `vault.<provider>:<chainId>:<shareToken>`.","example":"vault.savings:1:0x9d39a5de30e57443bff2a8307a4256c8797a3497"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"User wallet executing the action. `receiver` defaults to it.","example":"0xYourAddress"},{"name":"amount","in":"query","schema":{"type":"string"},"description":"Raw amount (wei). Required for deposit / withdraw / request-withdraw unless `isAll=true`. Denomination follows the owning route: assets by default, shares when `isShares=true`, PT units for Pendle.","example":"1000000000000000000"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient. Defaults to `operator`."},{"name":"payAsset","in":"query","schema":{"type":"string"},"description":"deposit — what the user actually pays, where the row publishes `acceptsPayAsset`. Zero address / `0xEEEE…` = native. On a vault row a payAsset that differs from the underlying triggers the aggregator conversion (see above); `amount` is then denominated in the PAY asset and `slippage` is required."},{"name":"receiveAsset","in":"query","schema":{"type":"string"},"description":"withdraw — what the user wants out, where the row publishes `acceptsReceiveAsset`."},{"name":"slippage","in":"query","schema":{"type":"string"},"description":"Basis points. Required wherever the row’s capability lists it in `requires` (any leg that settles on a book — Pendle PTs in both directions).","example":"50"},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"Interpret `amount` as shares (mint / redeem) instead of assets."},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Full-balance exit (vault withdraw). Not supported on swap-routed rows (Pendle) — pass the explicit amount there."},{"name":"simulate","in":"query","schema":{"type":"boolean"},"description":"Lending rows only — attach a post-trade simulation."}],"responses":{"200":{"description":"Action built by the owning handler. `permissions[]` first, then `transactions[]` in order.","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/vaults/deposit":{"get":{"tags":["Actions › Vaults"],"summary":"Vault deposit","description":"The **single deposit entry point** for every share-token vault. The protocol, interface, and underlying are resolved from the `vault` (share token), so a deposit needs only `vault` + `amount` + `operator`.\n\n**What it routes, all from the share token**\n\n- **ERC-4626 family** — savings, Morpho, Fluid, Euler-Earn, Silo, Gearbox, Lagoon, Yearn. Routes directly to `vault.deposit` when no wrap/swap is needed; otherwise the 1delta Composer (wraps native ETH, handles composition).\n- **LST / liquid-staking mints** — stETH, wstETH, weETH, rETH, stCELO, pumpBTC, Solv, Core, native-staked, … Resolved from the calldata-sdk registry by `vault` and built via the protocol's mint path, including protocol-specific prep (e.g. StakedCelo validator-group selection — see `validatorGroup`).\n- **ERC-7540** async and **ERC-7575** multi-asset vaults — detected via an ERC-165 probe.\n- **Pendle Principal Tokens** — a PT has no deposit leg at all: it is a zero-coupon bond BOUGHT on Pendle's AMM (and sold to exit). Detected from the token list, then built as a trade through the spot meta-aggregator, so the same `vault` + `amount` + `operator` call works. **`slippage` (bps) is REQUIRED here and has no default** — this leg settles against a pool, not a protocol-set price, so an unbounded fill accepts any price. `payAsset` is free (defaults to the PT's underlying); the chosen trade is in `transactions`, every quote in `alternatives`. This is what `/v1/data/earn` marks `via: 'swap'`.\n\n`underlying` and `interface` are read on-chain (`asset()` + ERC-165) when omitted; pass them explicitly to skip the reads. For Fluid **margin vaults** (NFT-position lending markets), use `/v1/actions/lending/deposit` instead.\n\n> **Not handled here:** GMX (GM/GLV) and Hypercore vaults are USD-denominated / multi-leg, not `asset()`-based share tokens, so they keep dedicated flows — GMX via [/v1/actions/vaults/gmx](#operation/vaultsGmx), Hypercore via `interface=hypercore` below. Passing one to the auto path returns `UNRESOLVED_UNDERLYING`.\n>\n> `/v1/actions/vaults/lst` and `/v1/actions/vaults/savings` remain as back-compatible aliases for their specific flows.\n\n**Execution mode** (`mode` query param, ERC-4626 path)\n\n- `auto` (default) — direct when `payAsset === underlying` and not native, otherwise composer.\n- `direct` — force direct; returns 400 when not eligible (e.g. native `payAsset`).\n- `proxy` — force the composer (legacy behavior).\n\nThe direct path is smaller (one call to the vault) and sidesteps the EVC indirection that the composer triggers. Approval target also changes: direct → the vault; proxy → the composer.\n\n**Native deposits**\n\nNative ETH is supported via the composer's wrap-then-deposit path (`mode=proxy`, or `auto` falls back automatically), and natively by most LST mints (`payAsset=0x0`). Fluid fToken vaults expose a payable `depositNative` that skips wrapping — opt in with `provider=fluid`.\n\n---\n\n## Async / multi-asset / Hypercore (`interface` query param)\n\nAuto-detected for ERC-7540/7575; only Hypercore needs an explicit `interface`:\n\n- `erc7540` — **async** vaults. Two-phase: `action=requestDeposit` (default, escrow assets) then `action=claimDeposit` (mint once fulfilled). Pass `controller` when it isn't `operator`. Track the pending request via [/v1/data/vaults/withdrawals](#operation/vaults-withdrawals).\n- `erc7575` — **multi-asset** vaults (share ≠ entry token). `isShares` switches deposit-by-assets vs mint-by-shares.\n- `hypercore` — **HyperLiquid** multi-leg deposit route (explicit only). `amount` is uint64 micro-USD (USDC); `source` picks the funding origin and the response is a structured `legs[]` array (each with its own `chainId`/`async`) rather than `transactions[]`.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"vault","in":"query","required":true,"schema":{"type":"string"},"description":"Share-token address — ERC-4626 vault or LST share token. The protocol/interface is resolved from it. (Alias: `shareToken`.)"},{"name":"underlying","in":"query","schema":{"type":"string"},"description":"Vault's `asset()` (real ERC-20 — never the zero address). Resolved on-chain from `vault` when omitted; required only for vaults without an `asset()` getter. Not needed for LST share tokens."},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei. Interpreted as assets unless `isShares=true`.","example":"1000000000000000000"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"User wallet executing the deposit (payer)","example":"0xYourAddress"},{"name":"payAsset","in":"query","schema":{"type":"string"},"description":"What the user actually pays. Defaults to `underlying`. Zero address or the `0xEEEE…` sentinel = native ETH (composer wraps before depositing)."},{"name":"slippage","in":"query","schema":{"type":"string"},"description":"Basis points. **Pendle PTs only, and REQUIRED there** — that leg is a trade against an AMM, so there is no protocol-set price to fall back on and no safe default. Ignored by every other vault family.","example":"50"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Share recipient. Defaults to `operator`. The composer mints shares directly to this address."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"When `true`, `amount` is treated as shares (mint) rather than assets (deposit)."},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Full-balance deposit. Relaxes the exit-sweep tolerance so dust does not revert the tx."},{"name":"provider","in":"query","schema":{"type":"string","enum":["fluid"]},"description":"Optional provider hint. Currently only `fluid` is recognized — it opts into Fluid's payable `depositNative` path when paying native ETH."},{"name":"mode","in":"query","schema":{"type":"string","enum":["auto","direct","proxy"],"default":"auto"},"description":"Execution mode. `auto` (default) routes direct when no wrap/swap is needed, composer otherwise. `direct` forces vault-direct (returns 400 if ineligible). `proxy` forces the composer."},{"name":"interface","in":"query","schema":{"type":"string","enum":["erc4626","erc7540","erc7575","hypercore","native-wnlp"]},"description":"Vault interface. Auto-detected via ERC-165 when omitted (`erc4626`/`erc7540`/`erc7575`), or from the savings registry for Native `wNLP` (`native-wnlp` → `depositAndWrap`, which also fills in `underlying` — wNLP has no `asset()` to probe). Pass `hypercore` explicitly for HyperLiquid (USD multi-leg). LST share tokens are routed by registry regardless of this param."},{"name":"action","in":"query","schema":{"type":"string","enum":["requestDeposit","claimDeposit"]},"description":"`interface=erc7540` only — `requestDeposit` (default) escrows assets; `claimDeposit` mints once fulfilled."},{"name":"controller","in":"query","schema":{"type":"string"},"description":"`interface=erc7540` controller, when not `operator`."},{"name":"validatorGroup","in":"query","schema":{"type":"string"},"description":"StakedCelo (stCELO) only — validator group to vote for. A `changeStrategy(group)` step is prepended (account-wide). Auto-selected when omitted and the caller is on the (reverting) default strategy; pass the zero address to force the default. Other LST options (`poolId`, `kind`, `referral`, …) from /vaults/lst are also accepted here."},{"name":"minUsddOut","in":"query","schema":{"type":"string"},"description":"sUSDD PSM zap only (`payAsset` = USDT/USDC into the sUSDD vault) — the 18-decimal USDD amount the deposit leg uses after the PSM swap. AUTO-QUOTED when omitted: the route reads the PSM’s live `tin` fee and the gem decimals and computes `amount·10^(18−dec) − fee`. Pass explicitly to pin a quote."},{"name":"source","in":"query","schema":{"type":"string","enum":["hypercore-perp","hypercore-spot","hyperevm","arbitrum"]},"description":"`interface=hypercore` deposit funding origin."},{"name":"hyperEvmUsdc","in":"query","schema":{"type":"string"},"description":"`interface=hypercore`, `source=hyperevm` — the HyperEVM USDC token address."}],"responses":{"200":{"description":"Transaction calldata + approval(s) for the vault deposit. `interface=hypercore` returns a structured `legs[]` route instead of `transactions[]`.","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/vaults/withdraw":{"get":{"tags":["Actions › Vaults"],"summary":"Vault withdraw","description":"Build calldata for an ERC-4626 vault withdraw. When the user wants the underlying back (no native unwrap), the dispatcher calls `vault.withdraw` / `vault.redeem` directly with no approval needed. When the receiver wants native ETH, falls back to the composer (unwraps WETH).\n\nThis endpoint targets **passive ERC-4626 vaults**. For Fluid's NFT-position margin vaults use `/v1/actions/lending/withdraw`.\n\n**Pendle Principal Tokens** exit the same way they are entered — by SELLING on Pendle's AMM, since a PT has no redeem leg before maturity. Detected from the token list and built as a trade through the spot meta-aggregator: `amount` is the PT amount, `receiveAsset` is free (defaults to the PT's underlying), and **`slippage` (bps) is REQUIRED** because the fill is priced by pool depth, not by the protocol.\n\n**Execution mode** (`mode` query param)\n\n- `auto` (default) — direct when `receiveAsset === underlying` and not native, otherwise composer.\n- `direct` — force direct; returns 400 when not eligible.\n- `proxy` — force the composer (legacy behavior).\n\nDirect withdraw skips the share-token approval entirely — the user calls `vault.withdraw(assets, receiver, owner=operator)` themselves and the vault uses its own balance check (`msg.sender == owner`).\n\n**Assets vs shares**\n\n- `isShares=false` (default): `amount` is assets — issues `vault.withdraw(assets, receiver, owner)`.\n- `isShares=true`: `amount` is shares — issues `vault.redeem(shares, receiver, owner)`.\n\n**Withdraw all** (`isAll=true`)\n\nTwo ways to drive a full-balance withdraw — both resolve the operator's vault-share balance and encode `vault.redeem(shares, …)`. They differ only in **where the share balance is read**:\n\n- **GET** + `isAll=true` — worker reads `balanceOf(vault, operator)` against its own configured RPC. Simple but only works when the worker can see the state (i.e. live mainnet, not fork). Returns `BALANCE_READ_FAILED` on RPC failure.\n- **POST** + `isAll=true` + body `{ \"sharesRaw\": \"<uint string>\" }` — caller pre-reads the balance against any RPC (fork, custom, premium) and supplies it. The worker just trusts and encodes. Mirrors the lending simulation pattern (`SimulationBody`).\n\nThe POST body's `amount` query param is ignored when `isAll=true`.\n\n---\n\n## Non-4626 interfaces (`interface` query param)\n\nDefault `erc4626` is the composer/direct path above. Otherwise:\n\n- `erc7540` — **async** redeem. `action=requestRedeem` (default) burns shares into the queue; `claimRedeem` / `claimWithdraw` settle once fulfilled. `controller` when not `operator`.\n- `erc7575` — **multi-asset**. Requires `share` (the share-token address); `isShares` toggles assets-out vs shares-in.\n- `hypercore` — **HyperLiquid** multi-leg withdraw route. `amount` is uint64 micro-USD; `destination` picks where the funds land. Returns a structured `legs[]` array. (Withdrawing out to Arbitrum is an L1 signed action, not EVM calldata — not offered here.)\n\n> Savings cooldowns (sUSDe) use [/v1/actions/vaults/savings](#operation/vaultsSavings); LST exits use [/v1/actions/vaults/lst](#operation/vaultsLst).","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"vault","in":"query","required":true,"schema":{"type":"string"},"description":"ERC-4626 share-token address"},{"name":"underlying","in":"query","required":true,"schema":{"type":"string"},"description":"Vault's `asset()`"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount in wei. Interpreted as assets unless `isShares=true`.","example":"1000000000000000000"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"User wallet executing the withdraw (share holder)","example":"0xYourAddress"},{"name":"receiveAsset","in":"query","schema":{"type":"string"},"description":"What the user receives. Defaults to `underlying`. Zero address or the `0xEEEE…` sentinel = native ETH (composer unwraps before forwarding)."},{"name":"slippage","in":"query","schema":{"type":"string"},"description":"Basis points. **Pendle PTs only, and REQUIRED there** — the exit is a sale into the PT pool, priced by its depth. Ignored by every other vault family.","example":"50"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Final recipient of the withdrawn assets. Defaults to `operator`."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"When `true`, `amount` is treated as shares (the composer calls `vault.redeem`)."},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Full-balance withdraw. Relaxes the sweep tolerance so dust does not revert the tx."},{"name":"mode","in":"query","schema":{"type":"string","enum":["auto","direct","proxy"],"default":"auto"},"description":"Execution mode. `auto` (default) routes direct when no unwrap is needed, composer otherwise. `direct` forces vault-direct (returns 400 if ineligible). `proxy` forces the composer."},{"name":"interface","in":"query","schema":{"type":"string","enum":["erc4626","erc7540","erc7575","hypercore","native-wnlp"],"default":"erc4626"},"description":"Vault interface. `erc4626` (default) = composer/direct path. Others dispatch to dedicated builders (see description). Native `wNLP` is auto-detected from the savings registry, which also fills in `underlying`."},{"name":"instant","in":"query","schema":{"type":"boolean"},"description":"`interface=native-wnlp` only — take the immediate `instantRedeem` instead of the free queue. Costs the vault’s `withdrawFeeBps` (100 bps by default), deducted from the underlying paid out rather than charged separately, and is capped by its reported `liquidity`. The default is the free queued leg: a fee is never charged unless asked for."},{"name":"action","in":"query","schema":{"type":"string","enum":["requestRedeem","claimRedeem","claimWithdraw"]},"description":"`interface=erc7540` only — `requestRedeem` (default), `claimRedeem`, or `claimWithdraw`. `interface=native-wnlp` — `requestWithdraw` (default), `claim`, or `cancel`."},{"name":"controller","in":"query","schema":{"type":"string"},"description":"`interface=erc7540` controller, when not `operator`."},{"name":"share","in":"query","schema":{"type":"string"},"description":"`interface=erc7575` — the share-token address (required for that interface)."},{"name":"destination","in":"query","schema":{"type":"string","enum":["hypercore-perp","hypercore-spot","hyperevm"]},"description":"`interface=hypercore` withdraw destination."}],"responses":{"200":{"description":"Transaction calldata + any approval(s) needed for the vault withdraw. `interface=hypercore` returns a structured `legs[]` route instead of `transactions[]`.","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["Actions › Vaults"],"summary":"Vault withdraw (with caller-supplied share balance)","description":"POST variant for `isAll=true` when the worker can't read the operator's share balance against its own RPC — fork tests, custom RPCs, sandbox endpoints. Same query params as GET; the body supplies the balance.\n\nWhen `isAll=true` the worker treats the body's `sharesRaw` as the truth and encodes `vault.redeem(sharesRaw, receiver, operator)`. The query `amount` is ignored on this path.\n\nWhen `isAll` is omitted/false the body is ignored (caller's `amount` is authoritative) — POST and GET behave identically.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"vault","in":"query","required":true,"schema":{"type":"string"},"description":"ERC-4626 share-token address"},{"name":"underlying","in":"query","required":true,"schema":{"type":"string"},"description":"Vault's `asset()`"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Ignored when `isAll=true` is set. Otherwise: amount in wei (assets unless `isShares=true`).","example":"0"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"User wallet (share holder)"},{"name":"receiveAsset","in":"query","schema":{"type":"string"},"description":"Underlying token to receive. Defaults to `underlying`."},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Final recipient. Defaults to `operator`."},{"name":"isShares","in":"query","schema":{"type":"boolean"},"description":"When `true`, `amount` is shares (ignored if `isAll=true`)."},{"name":"isAll","in":"query","schema":{"type":"boolean"},"description":"Full-balance withdraw. Body must include `sharesRaw`."},{"name":"mode","in":"query","schema":{"type":"string","enum":["auto","direct","proxy"],"default":"auto"},"description":"Same as GET — execution mode selector."}],"requestBody":{"required":true,"description":"Caller-supplied state for `isAll=true`.","content":{"application/json":{"schema":{"type":"object","required":["sharesRaw"],"properties":{"sharesRaw":{"type":"string","pattern":"^\\d+$","description":"Operator's current vault-share balance as a decimal uint string. The worker uses this directly as the redeem amount.","example":"993566017"}}}}}},"responses":{"200":{"description":"Transaction calldata + any approval(s) needed for the vault withdraw","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/vaults/lst":{"get":{"tags":["Actions › Vaults"],"summary":"LST / LRT mint · withdraw-request · claim · cancel","operationId":"vaultsLst","description":"Build calldata for **LST / LRT** (and Lagoon) actions — mint, withdraw-request, claim, and cancel — via the unified LST dispatchers. The protocol is resolved from the calldata-sdk registry by `(chainId, shareToken)`, or forced with `kind=` (required for Lagoon — `kind=lagoon&mode=sync|async` — and any unregistered vault).\n\n> **Deposits also work through the unified [/v1/actions/vaults/deposit](#operation/vaultsDeposit)** — pass the LST share token as `vault` and it routes here automatically (no need to know it's an LST). This endpoint stays as the explicit LST route and is the home for the **exit-side** actions (`request-withdraw` / `claim` / `cancel`), which the generic deposit endpoint does not cover.\n\n---\n\n## How an integrator knows what to pay with (`acceptedInputs`)\n\nLSTs are **not** uniform ERC-4626 vaults — each protocol accepts a different set of pay assets (native ETH, an unwrapped sibling like stETH/eETH, or specific ERC-20s) and some paths require extra slippage/intermediate options. Rather than hard-coding this, **fetch the vault data first and let it drive the request**:\n\n1. `GET /v1/data/vaults?chainId=…&providers=lst` → each LST carries a `providerMeta` block:\n   - `isMintable` — `false` ⇒ no on-chain mint (e.g. cbETH); don't call this endpoint.\n   - `mintContract` — the deposit target.\n   - `exchangeRate` / `convertToShares` — to compute expected output and a `minOut`.\n   - **`acceptedInputs[]`** — the machine-readable accept-set (see below).\n2. Pick the `acceptedInputs` entry whose `asset` matches what the user holds.\n3. Call this endpoint with `action=deposit`, `payAsset=<that asset>`, and the entry's `needs[]` supplied as query params.\n\n### `acceptedInputs[]` entry shape\n\n| field | meaning |\n|---|---|\n| `asset` | `\"native\"` (pay with the chain coin → pass `payAsset=0x000…000`) or a lowercased ERC-20 address to pass as `payAsset`. |\n| `symbol` | optional UI hint (e.g. `stETH`). |\n| `mode` | `direct` (single call) · `wrap` (approve + wrap a base LST the user already holds) · `submit-wrap` (native → base → wrapped; needs the realised base amount). |\n| `needs` | option keys the caller **must** pass for this path (e.g. `[\"minMETHAmount\"]`, `[\"eEthAmount\"]`, `[\"stEthAmount\"]`). Absent ⇒ none. |\n\n**Example** — wstETH advertises three paths:\n```json\n\"acceptedInputs\": [\n  { \"asset\": \"native\", \"mode\": \"direct\" },\n  { \"asset\": \"0xae7ab9…\", \"symbol\": \"stETH\", \"mode\": \"wrap\" },\n  { \"asset\": \"native\", \"mode\": \"submit-wrap\", \"needs\": [\"stEthAmount\"] }\n]\n```\n- Pay with ETH → `payAsset=0x000…000` (one-step `receive()`).\n- Pay with stETH you already hold → `payAsset=0xae7ab9…` (approve + wrap).\n\n---\n\n## Multi-step results\n\n`wrap` / `submit-wrap` paths produce **multiple ordered transactions**. The response's `actions.transactions[]` is the sequence to execute **in order**, and `actions.permissions[]` carries any ERC-20 approvals to run **first**. Single-step mints return one transaction and (for native) no permissions. Always iterate `transactions[]`; never assume a single tx.\n\n---\n\n## Slippage / required options\n\nPaths whose `needs` includes a min-out (`minMETHAmount`, `minRSETHAmountExpected`, …) require the caller to compute the floor from the data's `exchangeRate` and pass it. Native one-step mints (stETH, ETHx, ezETH, ynETH, pufETH, wstETH-receive) need nothing beyond `amount`.\n\n---\n\n## Actions\n\n- `deposit` — mint. `amount` required; `payAsset` (default native); path options per `acceptedInputs[].needs`.\n- `request-withdraw` — start an exit (queue/cooldown). `amount` required; `inputAsset?`, `outputAsset?`, `owner?`.\n- `claim` — settle a matured exit. Identifier params: `requestIds`/`hints`/`amounts` (CSV), `tokenId`, `id`, `shares`, `assets`, `controller`, `user`, `recipient`.\n- `cancel` — cancel a pending request (same identifier params).","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"action","in":"query","schema":{"type":"string","enum":["deposit","request-withdraw","claim","cancel"],"default":"deposit"},"description":"Operation to build."},{"name":"shareToken","in":"query","required":true,"schema":{"type":"string"},"description":"LST share-token address (the token the user wants to mint / exit).","example":"0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"User wallet executing the action (payer). `receiver` defaults to it.","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient of the minted shares / claimed assets. Defaults to `operator`."},{"name":"amount","in":"query","schema":{"type":"string"},"description":"Required for `deposit` and `request-withdraw`. Wei, in the `payAsset` decimals.","example":"1000000000000000000"},{"name":"payAsset","in":"query","schema":{"type":"string"},"description":"deposit: what the user pays. Zero address (or `0xEEEE…`) = native; otherwise an ERC-20 from the LST's `acceptedInputs[].asset`. Defaults to native."},{"name":"kind","in":"query","schema":{"type":"string"},"description":"Protocol override — required for Lagoon (`lagoon`) and any vault not in the static registry. Otherwise resolved from `(chainId, shareToken)`."},{"name":"mode","in":"query","schema":{"type":"string","enum":["sync","async"]},"description":"Lagoon deposit mode."},{"name":"minOut","in":"query","schema":{"type":"integer"},"description":"Generic slippage floor (wei) where the protocol path accepts one. For protocol-specific names see the LST `acceptedInputs[].needs`."},{"name":"referral","in":"query","schema":{"type":"string"},"description":"Optional referral address (protocols that support it)."},{"name":"stEthAmount","in":"query","schema":{"type":"integer"},"description":"wstETH `submit-wrap` path: the realised stETH amount (read `stETH.balanceOf` after the submit leg)."},{"name":"eEthAmount","in":"query","schema":{"type":"integer"},"description":"weETH `submit-wrap` path: the realised eETH amount (read after the deposit leg)."},{"name":"vault","in":"query","schema":{"type":"string"},"description":"StakeWise per-vault target (and other per-vault protocols)."},{"name":"poolId","in":"query","schema":{"type":"string"},"description":"Solv pool selector."},{"name":"validatorGroup","in":"query","schema":{"type":"string"},"description":"StakedCelo (stCELO) deposit: validator group to vote for — a `changeStrategy(group)` step is prepended (account-wide). Auto-selected when omitted and the caller is on the (reverting) default strategy; zero address forces the default."},{"name":"rewardVault","in":"query","schema":{"type":"string"},"description":"BeraPaw reward-vault target."},{"name":"stakingToken","in":"query","schema":{"type":"string"},"description":"Bearn staking-token target."},{"name":"inputAsset","in":"query","schema":{"type":"string"},"description":"request-withdraw: asset being burned, when the protocol needs it disambiguated."},{"name":"outputAsset","in":"query","schema":{"type":"string"},"description":"request-withdraw / claim: desired exit asset, when the protocol supports a choice."},{"name":"owner","in":"query","schema":{"type":"string"},"description":"request-withdraw: position owner, when not `operator`."},{"name":"requestIds","in":"query","schema":{"type":"string"},"description":"claim/cancel: CSV of withdrawal-request ids."},{"name":"hints","in":"query","schema":{"type":"string"},"description":"claim/cancel: CSV of finalization hints (Lido)."},{"name":"amounts","in":"query","schema":{"type":"string"},"description":"claim/cancel: CSV of per-request amounts."},{"name":"tokenId","in":"query","schema":{"type":"integer"},"description":"claim/cancel: NFT request id."},{"name":"id","in":"query","schema":{"type":"integer"},"description":"claim/cancel: numeric request id."},{"name":"shares","in":"query","schema":{"type":"integer"},"description":"claim: share amount (7540/4626 savings)."},{"name":"assets","in":"query","schema":{"type":"integer"},"description":"claim: asset amount."},{"name":"controller","in":"query","schema":{"type":"string"},"description":"claim: 7540 controller, when not `receiver`."},{"name":"user","in":"query","schema":{"type":"string"},"description":"claim/cancel: position user, when not `operator`."},{"name":"recipient","in":"query","schema":{"type":"string"},"description":"claim: payout recipient, when supported."}],"responses":{"200":{"description":"Ordered `transactions[]` (execute in sequence) + ERC-20 `permissions[]` (execute first). Single-step mints return one transaction.","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/vaults/savings":{"get":{"tags":["Actions › Vaults"],"summary":"Savings-vault request-withdraw · claim · deposit · cancel","operationId":"vaultsSavings","description":"Build calldata for **savings-vault** exits — the cooldown / async-redeem mechanics that plain ERC-4626 `withdraw` can't express (Ethena `sUSDe`, `erc7540` async vaults, instant `erc4626` redeem, and Native Credit Pool `wNLP`) — plus the deposit leg for the one family that isn't ERC-4626. The protocol is resolved from the calldata-sdk savings registry by `(chainId, shareToken)`, or forced with `kind=ethena|erc7540|erc4626|native-wnlp`.\n\n> **ERC-4626 savings deposits go through [/v1/actions/vaults/deposit](#operation/vaultsDeposit).** Native `wNLP` works on *either* route — `/vaults/deposit` auto-detects it from the registry and needs no `underlying`.\n\n## Actions\n\n- `request-withdraw` (default) — start the exit:\n  - **Ethena** → `cooldownShares(amount)` (or `cooldownAssets` when `byAssets=true`); escrows the assets for the cooldown window.\n  - **erc7540** → `requestRedeem`.\n  - **erc4626** → instant `redeem` (no cooldown).\n  - **native-wnlp** → `WithdrawQueue.requestWithdrawal(amount)` — the **free** leg; the payout is snapshotted at request time and claimable after the queue window. Pass `instant=true` for `instantRedeem` instead: one transaction, no wait, but it costs the vault's `withdrawFeeBps` (100 bps by default) — deducted from the underlying paid out, never charged as a separate transfer — and is capped by its reported `liquidity`.\n- `claim` — settle a matured exit:\n  - **Ethena** → `unstake(receiver)`.\n  - **erc7540 / erc4626** → `redeem` (pass `shares`).\n  - **native-wnlp** → `claimWithdrawalTo(receiver)`; no `shares` — the queue always pays the full request.\n- `deposit` — **native-wnlp only** (`depositAndWrap(receiver, amount)`). Every other kind is a plain ERC-4626 mint and is rejected here with a pointer to `/vaults/deposit`.\n- `cancel` — **native-wnlp only**; withdraw an open queued request and take the shares back. One request per address, so cancel is also how you resize one.\n\nTrack pending requests via [/v1/data/vaults/withdrawals](#operation/vaults-withdrawals).","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID","example":"1"},{"name":"action","in":"query","schema":{"type":"string","enum":["request-withdraw","claim","deposit","cancel"],"default":"request-withdraw"},"description":"Operation to build. `deposit` and `cancel` are Native `wNLP` only."},{"name":"shareToken","in":"query","required":true,"schema":{"type":"string"},"description":"Savings share-token address (e.g. sUSDe `0x9d39a5de30e57443bff2a8307a4256c8797a3497`).","example":"0x9d39a5de30e57443bff2a8307a4256c8797a3497"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"User wallet executing the action (share holder). `receiver` defaults to it.","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient of the claimed assets. Defaults to `operator`. Kinds whose on-chain calls act on msg.sender only (wren, bitway) reject a value that differs from `operator` with a 400."},{"name":"kind","in":"query","schema":{"type":"string","enum":["ethena","erc7540","erc4626","native-wnlp","susd3","strata","apyx","maple","frankencoin","yieldbasis","wren","hyperbeat","bitway","vesper"]},"description":"Protocol override. Otherwise resolved from `(chainId, shareToken)`."},{"name":"queueId","in":"query","schema":{"type":"string"},"description":"claim / cancel, **Bitway only** — the claim-queue id returned by `requestClaim` (also in its `RequestClaim` event).","example":"106031"},{"name":"amount","in":"query","schema":{"type":"string"},"description":"request-withdraw: shares to cool down (or underlying assets when `byAssets=true`). deposit: underlying to wrap. Wei.","example":"1000000000000000000"},{"name":"byAssets","in":"query","schema":{"type":"boolean"},"description":"request-withdraw, **Ethena only** — interpret `amount` as the underlying asset amount (`cooldownAssets`) instead of shares (`cooldownShares`)."},{"name":"instant","in":"query","schema":{"type":"boolean"},"description":"request-withdraw, **native-wnlp only** — take the immediate `instantRedeem` instead of the free queue. Costs the vault’s `withdrawFeeBps` (deducted from the underlying paid out) and is capped by its `liquidity`; check both on [/v1/data/vaults](#operation/vaults) first."},{"name":"underlying","in":"query","schema":{"type":"string"},"description":"deposit, **native-wnlp only** — the asset to wrap, when the pool is too new to be in the registry. Otherwise resolved automatically."},{"name":"shares","in":"query","schema":{"type":"integer"},"description":"claim: share amount to redeem (erc7540 / erc4626)."},{"name":"owner","in":"query","schema":{"type":"string"},"description":"request-withdraw: share owner, when not `operator` (erc4626 redeem)."},{"name":"controller","in":"query","schema":{"type":"string"},"description":"erc7540 controller, when not `operator`."}],"responses":{"200":{"description":"Ordered `transactions[]` + ERC-20 `permissions[]` (run first). Each step carries its builder label as `description`.","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/vaults/gmx":{"get":{"tags":["Actions › Vaults"],"summary":"GMX V2 GM / GLV deposit · withdraw · cancel","operationId":"vaultsGmx","description":"Build calldata for **GMX V2** GM-market / GLV liquidity actions (Arbitrum `42161`, Avalanche `43114`). These are **async**: each call returns a single `ExchangeRouter` (GM) / `GlvRouter` (GLV) `multicall` transaction (plus any ERC-20 approval to the GMX Router), and a **keeper** executes the request a few seconds later. The deposit/withdraw tx must carry a native `executionFee` (the keeper's gas reimbursement) — GMX reverts if it's underpaid.\n\n## Actions\n\n- `deposit` (default) — add liquidity. Needs `longToken` + `shortToken` (the market legs), `payAsset` (the token paid in), `amount`, `executionFee`.\n- `withdraw` — remove liquidity (burn GM/GLV tokens). Needs `amount` (GM/GLV tokens), `executionFee`.\n- `cancel` — cancel a still-pending deposit/withdrawal ticket. Needs `request=deposit|withdrawal` and the 32-byte `key` (from [/v1/data/vaults/gmx](#operation/vaults-gmx)).\n\n## GM vs GLV (`kind`)\n\n- `gm` (default) — a single GM market; `market` defaults to `vault`.\n- `glv` — an auto-rebalancing GLV vault; you **must** name the GM `market` within the GLV.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID — `42161` (Arbitrum) or `43114` (Avalanche).","example":"42161"},{"name":"action","in":"query","schema":{"type":"string","enum":["deposit","withdraw","cancel"],"default":"deposit"},"description":"Operation to build."},{"name":"kind","in":"query","schema":{"type":"string","enum":["gm","glv"],"default":"gm"},"description":"Pool kind."},{"name":"vault","in":"query","required":true,"schema":{"type":"string"},"description":"GM market token (`gm`) or GLV token (`glv`)."},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"Caller wallet (deposit/withdraw). `receiver` defaults to it.","example":"0xYourAddress"},{"name":"receiver","in":"query","schema":{"type":"string"},"description":"Recipient. Defaults to `operator`."},{"name":"amount","in":"query","schema":{"type":"string"},"description":"deposit: input-token amount. withdraw: GM/GLV tokens to burn. Wei.","example":"1000000000000000000"},{"name":"executionFee","in":"query","schema":{"type":"string"},"description":"deposit/withdraw: native keeper fee (wei), forwarded as the tx `value`. GMX rejects underpaid requests — quote it from the GMX UI/SDK.","example":"75000000000000"},{"name":"market","in":"query","schema":{"type":"string"},"description":"GLV: **required** — the GM market within the GLV. GM: defaults to `vault`."},{"name":"longToken","in":"query","schema":{"type":"string"},"description":"deposit: **required** — the market long leg."},{"name":"shortToken","in":"query","schema":{"type":"string"},"description":"deposit: **required** — the market short leg."},{"name":"payAsset","in":"query","schema":{"type":"string"},"description":"deposit: token paid in. Zero / `0xEEEE…` = native. Defaults to `longToken`."},{"name":"minOut","in":"query","schema":{"type":"integer"},"description":"Slippage floor on output GM/GLV (deposit) or output token (withdraw)."},{"name":"minLongTokenAmount","in":"query","schema":{"type":"integer"},"description":"withdraw: min long-token received."},{"name":"minShortTokenAmount","in":"query","schema":{"type":"integer"},"description":"withdraw: min short-token received."},{"name":"isMarketTokenDeposit","in":"query","schema":{"type":"boolean"},"description":"deposit: the input is already GM tokens (GLV deposit of an existing GM position)."},{"name":"shouldUnwrapNativeToken","in":"query","schema":{"type":"boolean"},"description":"withdraw: unwrap WETH/WAVAX output to native."},{"name":"uiFeeReceiver","in":"query","schema":{"type":"string"},"description":"Optional UI-fee recipient."},{"name":"callbackGasLimit","in":"query","schema":{"type":"integer"},"description":"Optional gas limit for the keeper callback."},{"name":"request","in":"query","schema":{"type":"string","enum":["deposit","withdrawal"]},"description":"cancel: which pending ticket to cancel."},{"name":"key","in":"query","schema":{"type":"string"},"description":"cancel: 32-byte request key (from `/v1/data/vaults/gmx`)."}],"responses":{"200":{"description":"A single `ExchangeRouter`/`GlvRouter` `multicall` transaction (plus any Router approval). A keeper executes the request shortly after.","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/midnight/make":{"get":{"tags":["Actions › Midnight"],"summary":"Make offer (build typed-data)","operationId":"midnight-make","description":"**MAKE — step 1 of 2.** Build the EIP-712 typed-data for a limit offer at your own rate.\n\n**Morpho Midnight is an order-book lender** — liquidity is a book of maker offers, not a pool — so there are two ways to interact:\n\n- **TAKE** (fill existing offers): use the **standard lending actions**, which route to Midnight automatically for a `MORPHO_MIDNIGHT_<id>` market. There is no separate \"take\" endpoint.\n  - **lend** → [`/v1/actions/lending/deposit`](#operation/lending-deposit) — fills the **ask** side (supply offers)\n  - **borrow** → [`/v1/actions/lending/borrow`](#operation/lending-borrow) — fills the **bid** side (demand offers)\n  - **repay** / **withdraw** → [`/v1/actions/lending/repay`](#operation/lending-repay) · [`/withdraw`](#operation/lending-withdraw)\n  - Read the live two-sided ladder from [`/v1/data/lending/latest?includeOffers=true`](#operation/lending-latest) — each order-book market's loan leg carries `offers` (bids) and `lendOffers` (asks), best-first, with per-level `aprPct`, `assets`, and `cumulativeAssets`.\n- **MAKE** (post your own limit offer at a chosen rate): the endpoints in this section (`/v1/actions/midnight/*`) plus [`/v1/data/lending/orders`](#operation/lending-orders) to list and cancel them.\n\n**Make flow (worker-assisted signing):**\n1. `GET /v1/actions/midnight/make` → returns `typedData`, the echoed `inputs`, and a one-time `authorization` tx.\n2. The maker signs `typedData` with their wallet (`wallet.signTypedData`).\n3. `POST /v1/actions/midnight/finalize` with `{ inputs, signature }` → the on-chain transaction that publishes the offer to the Midnight mempool.\n\nIf `authorization` is present and the maker has **not** yet authorized the ratifier (read `isAuthorized` on-chain), send that one-time `setIsAuthorized` transaction **before** finalizing. The APR you request is snapped to an order-book tick — `aprPctSnapped` is the exact rate the offer will quote.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID (alias: `chain`). Midnight is Base-only today.","example":"8453"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"`MORPHO_MIDNIGHT_<id>` lender key (the market to post into).","example":"MORPHO_MIDNIGHT_0xABC…"},{"name":"side","in":"query","required":true,"schema":{"type":"string","enum":["lend","borrow"]},"description":"`lend` posts a bid (you lend when taken); `borrow` posts an ask (you borrow when taken)."},{"name":"aprPct","in":"query","required":true,"schema":{"type":"number"},"description":"Your target APR in percent (alias: `rate`). Snapped to an order-book tick; see `aprPctSnapped` in the response.","example":3.49},{"name":"size","in":"query","required":true,"schema":{"type":"string"},"description":"Offer size in loan-token units (integer wei).","example":"1000000000"},{"name":"expiry","in":"query","required":true,"schema":{"type":"integer"},"description":"Offer expiry, unix seconds. Must be in the future and ≤ the market maturity.","example":1793491200},{"name":"account","in":"query","required":true,"schema":{"type":"string"},"description":"Maker (offer owner) address."}],"responses":{"200":{"description":"Offer typed-data, echoed inputs, and one-time authorization","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/MidnightMakeResponse"},"actions":{"type":"null"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/midnight/finalize":{"post":{"tags":["Actions › Midnight"],"summary":"Make offer (finalize + publish)","operationId":"midnight-finalize","description":"**MAKE — step 2 of 2.** Ratify the maker's signature and encode the on-chain payload that publishes the offer to the Midnight mempool (where the offer API indexes it).\n\nPOST the `inputs` returned verbatim by `/v1/actions/midnight/make` together with the maker's `signature`. The worker rebuilds the identical offer tree from `inputs` and binds the signature to it — any change to `inputs` fails ratification on-chain. Returns the single publish transaction in `actions.transactions`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MidnightFinalizeRequest"}}}},"responses":{"200":{"description":"The mempool publish transaction (in `actions.transactions`)","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/midnight/cancel":{"get":{"tags":["Actions › Midnight"],"summary":"Cancel offer","operationId":"midnight-cancel","description":"Cancel one of the maker's open offers. Calls `cancelRoot(maker, root)` on the Midnight EcrecoverRatifier, invalidating the offer under that tree `root`. A plain on-chain transaction by the maker — no signature needed.\n\nGet the offer's `root` from [`/v1/data/lending/orders`](#operation/lending-orders) (the order's `id`).","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID (alias: `chain`).","example":"8453"},{"name":"account","in":"query","required":true,"schema":{"type":"string"},"description":"Maker (offer owner) address."},{"name":"root","in":"query","required":true,"schema":{"type":"string"},"description":"Offer tree root (bytes32) — the order `id` from `/v1/data/lending/orders`."}],"responses":{"200":{"description":"The `cancelRoot` transaction (in `actions.transactions`)","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/term/offer":{"post":{"tags":["Actions › Term"],"summary":"Lock lend offers (auction)","operationId":"term-offer","description":"**AUCTION LEND — step 1 of 2.** Build the `lockOffers` transaction that submits sealed lend offers to the repo's active `TermAuctionOfferLocker`. Reveal them after the auction's reveal window opens via `/v1/actions/term/reveal-offers`.\n\n**Term Finance is a fixed-rate tri-party-repo lender** (NOT a Morpho Blue fork) with two lend surfaces:\n\n- **Secondary order book** (`RepoTokenLinkedList`): buy/sell repo tokens continuously. LEND (buy a repo token) routes through the **standard lending action** [`/v1/actions/lending/deposit`](#operation/lending-deposit) for a `TERM_FINANCE_<id>` market. List/cancel your own repo tokens with `/v1/actions/term/list` · `/unlist`, and read your listings + auction submissions from the unified [`/v1/data/lending/orders`](#operation/lending-orders).\n- **Primary auctions** (sealed-bid): submit a lend **offer** or borrow **bid**, then **reveal** it. The endpoints in this section (`/v1/actions/term/*`) build those transactions.\n\n**Servicing** (repay / redeem / collateral) always uses the standard lending actions: [repay](#operation/lending-repay) · [withdraw](#operation/lending-withdraw) · [deposit](#operation/lending-deposit) (collateral leg).\n\n**Sealed commitments:** `offerPriceHash` / `bidPriceHash` = `keccak(price, nonce)` are computed **client-side** so the server never sees your secret price/nonce before reveal.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TermOfferRequest"}}}},"responses":{"200":{"description":"The `lockOffers` transaction + purchase-token approval","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/term/bid":{"post":{"tags":["Actions › Term"],"summary":"Lock borrow bids (auction)","operationId":"term-bid","description":"**AUCTION BORROW — step 1 of 2.** Build the `lockBids` transaction that submits sealed borrow bids (escrowing collateral) to the repo's active `TermAuctionBidLocker`. Reveal them via `/v1/actions/term/reveal-bids`.\n\n**Term Finance is a fixed-rate tri-party-repo lender** (NOT a Morpho Blue fork) with two lend surfaces:\n\n- **Secondary order book** (`RepoTokenLinkedList`): buy/sell repo tokens continuously. LEND (buy a repo token) routes through the **standard lending action** [`/v1/actions/lending/deposit`](#operation/lending-deposit) for a `TERM_FINANCE_<id>` market. List/cancel your own repo tokens with `/v1/actions/term/list` · `/unlist`, and read your listings + auction submissions from the unified [`/v1/data/lending/orders`](#operation/lending-orders).\n- **Primary auctions** (sealed-bid): submit a lend **offer** or borrow **bid**, then **reveal** it. The endpoints in this section (`/v1/actions/term/*`) build those transactions.\n\n**Servicing** (repay / redeem / collateral) always uses the standard lending actions: [repay](#operation/lending-repay) · [withdraw](#operation/lending-withdraw) · [deposit](#operation/lending-deposit) (collateral leg).\n\n**Sealed commitments:** `offerPriceHash` / `bidPriceHash` = `keccak(price, nonce)` are computed **client-side** so the server never sees your secret price/nonce before reveal.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TermBidRequest"}}}},"responses":{"200":{"description":"The `lockBids` transaction + per-collateral approvals","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/term/reveal-offers":{"post":{"tags":["Actions › Term"],"summary":"Reveal locked offers","operationId":"term-reveal-offers","description":"**AUCTION LEND — step 2 of 2.** Reveal previously-locked offers (opens the sealed price + nonce) after the reveal window opens.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TermRevealRequest"}}}},"responses":{"200":{"description":"The `revealOffers` transaction","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/term/reveal-bids":{"post":{"tags":["Actions › Term"],"summary":"Reveal locked bids","operationId":"term-reveal-bids","description":"**AUCTION BORROW — step 2 of 2.** Reveal previously-locked bids after the reveal window opens.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TermRevealRequest"}}}},"responses":{"200":{"description":"The `revealBids` transaction","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/term/unlock-offers":{"get":{"tags":["Actions › Term"],"summary":"Unlock (cancel) offers","operationId":"term-unlock-offers","description":"Cancel unrevealed offers, reclaiming the escrowed purchase token.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID (alias: `chain`).","example":"1"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"`TERM_FINANCE_<id>` lender key (the repo/auction).","example":"TERM_FINANCE_0xABC…"},{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated bytes32 offer/bid ids to unlock."}],"responses":{"200":{"description":"The `unlockOffers` transaction","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/term/unlock-bids":{"get":{"tags":["Actions › Term"],"summary":"Unlock (cancel) bids","operationId":"term-unlock-bids","description":"Cancel unrevealed bids, reclaiming escrowed collateral.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID (alias: `chain`).","example":"1"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"`TERM_FINANCE_<id>` lender key (the repo/auction).","example":"TERM_FINANCE_0xABC…"},{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated bytes32 offer/bid ids to unlock."}],"responses":{"200":{"description":"The `unlockBids` transaction","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/term/list":{"get":{"tags":["Actions › Term"],"summary":"Create a secondary listing","operationId":"term-list","description":"List repo tokens for sale on the secondary `RepoTokenLinkedList` order book (early exit). Escrows the repo token, so the response includes a repo-token approval.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID (alias: `chain`).","example":"1"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"`TERM_FINANCE_<id>` lender key.","example":"TERM_FINANCE_0xABC…"},{"name":"amount","in":"query","required":true,"schema":{"type":"string"},"description":"Amount to list, repo-token units (integer wei).","example":"1000000000"}],"responses":{"200":{"description":"The `createListing` transaction + repo-token approval","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/term/unlist":{"get":{"tags":["Actions › Term"],"summary":"Cancel a secondary listing","operationId":"term-unlist","description":"Cancel one of your secondary-market listings by `listingId` (the order `id` from [`/v1/data/lending/orders`](#operation/lending-orders)).","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain ID (alias: `chain`).","example":"1"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"`TERM_FINANCE_<id>` lender key.","example":"TERM_FINANCE_0xABC…"},{"name":"listingId","in":"query","required":true,"schema":{"type":"string"},"description":"The listing id to cancel."},{"name":"skipRedeem","in":"query","schema":{"type":"boolean"},"description":"Skip auto-redeem of the returned repo token (default false)."}],"responses":{"200":{"description":"The `cancelListing` transaction","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/actions/liquity/open":{"post":{"tags":["Actions › Liquity"],"summary":"Open a trove","operationId":"liquity-open","description":"Open a trove: deposit collateral and mint stable debt at a user-set annual interest rate.\n\n**Liquity V2 family is a pooled CDP** — one branch per collateral, troves = sub-accounts (`troveId`, the sub-account id from user data), user-set adjustable interest rates, and a per-branch Stability Pool as the stable-token earn side. Deployments: `LIQUITY_V2` (Ethereum) + friendly forks (`USDAF`, `FELIX`, `NERITE`, `QUILL`, `ENOSYS_LOANS`, `SONETA`, `EBISU`) — all share this surface; per-fork parameters (min debt, rate bounds, gas compensation) come from metadata.\n\n**Standard lending actions cover the day-to-day ops** for a `<DEPLOYMENT>_<chainId>_<COLL_INDEX>` market key:\n- stable-token deposit/withdraw → Stability Pool (earn; `isAll` withdraw uses the protocol sentinel)\n- collateral deposit/withdraw → `addColl`/`withdrawColl` (pass `troveId`)\n- borrow → `withdrawBold` (pass `troveId`; upfront-fee guard quoted automatically, override with `maxUpfrontFee`)\n- repay → `repayBold` clamped to `entireDebt − minDebt`; `isAll=true` routes to `closeTrove` (burns the full live debt from the wallet, returns collateral + the WETH gas compensation)\n\nThe endpoints in this section handle the trove lifecycle ops that need more than one amount or no amount at all. Fees to know: the upfront borrowing fee (≈7 days of average branch interest on any debt increase) and the premature rate-adjustment fee (same formula on the WHOLE debt when changing the rate within the cooldown). Repaying itself is always free.\n\nValidates the deployment's min debt + rate bounds, quotes SortedTroves insert hints and the `maxUpfrontFee` guard on-chain, and picks the next free owner index. Returns the transaction plus the collateral (+ gas-compensation WETH) approvals.\n\n`interestRate` is OPTIONAL: omit it and the trove opens at the **branch average** user-set rate (the rate the market data quotes as the branch borrow rate — mid-pack in the redemption queue). The applied rate is returned as `interestRate` in the response `data`; change it later with `POST /v1/actions/liquity/set-rate`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["chainId","lender","account","collAmount","amount"],"properties":{"chainId":{"type":"string","example":"1"},"lender":{"type":"string","description":"Per-branch key, e.g. LIQUITY_V2_1_0","example":"LIQUITY_V2_1_0"},"account":{"type":"string","description":"Trove owner (tx sender)"},"collAmount":{"type":"string","description":"Collateral amount, raw units"},"amount":{"type":"string","description":"Stable debt to mint, raw units (≥ deployment minDebt)"},"interestRate":{"type":"string","description":"Optional user-set annual rate, WAD (1e16 = 1%). Default: the branch average rate.","example":"60000000000000000"},"ownerIndex":{"type":"string","description":"Optional explicit owner index (default: next free)"},"maxUpfrontFee":{"type":"string","description":"Optional fee-guard override, raw stable units"}}}}}},"responses":{"200":{"200":{"description":"Open-trove transaction + approvals","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}}},"400":{"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"/v1/actions/liquity/close":{"get":{"tags":["Actions › Liquity"],"summary":"Close a trove","operationId":"liquity-close","description":"Close a trove: burns the FULL live `entireDebt` (incl. per-block interest accrual — keep a small stable buffer above any quote) from the sender and returns all collateral plus the gas compensation. Equivalent to `lending/repay` with `isAll=true`.\n\n**Liquity V2 family is a pooled CDP** — one branch per collateral, troves = sub-accounts (`troveId`, the sub-account id from user data), user-set adjustable interest rates, and a per-branch Stability Pool as the stable-token earn side. Deployments: `LIQUITY_V2` (Ethereum) + friendly forks (`USDAF`, `FELIX`, `NERITE`, `QUILL`, `ENOSYS_LOANS`, `SONETA`, `EBISU`) — all share this surface; per-fork parameters (min debt, rate bounds, gas compensation) come from metadata.\n\n**Standard lending actions cover the day-to-day ops** for a `<DEPLOYMENT>_<chainId>_<COLL_INDEX>` market key:\n- stable-token deposit/withdraw → Stability Pool (earn; `isAll` withdraw uses the protocol sentinel)\n- collateral deposit/withdraw → `addColl`/`withdrawColl` (pass `troveId`)\n- borrow → `withdrawBold` (pass `troveId`; upfront-fee guard quoted automatically, override with `maxUpfrontFee`)\n- repay → `repayBold` clamped to `entireDebt − minDebt`; `isAll=true` routes to `closeTrove` (burns the full live debt from the wallet, returns collateral + the WETH gas compensation)\n\nThe endpoints in this section handle the trove lifecycle ops that need more than one amount or no amount at all. Fees to know: the upfront borrowing fee (≈7 days of average branch interest on any debt increase) and the premature rate-adjustment fee (same formula on the WHOLE debt when changing the rate within the cooldown). Repaying itself is always free.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain id"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"Per-branch key, e.g. LIQUITY_V2_1_0"},{"name":"troveId","in":"query","required":true,"schema":{"type":"string"},"description":"Trove id (sub-account id from user data)"}],"responses":{"200":{"200":{"description":"Close-trove transaction","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}}},"400":{"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"/v1/actions/liquity/set-rate":{"post":{"tags":["Actions › Liquity"],"summary":"Adjust the user-set interest rate","operationId":"liquity-set-rate","description":"Change a trove's user-set annual interest rate. Free after the deployment's cooldown (7 days vanilla); within it, the upfront fee applies to the WHOLE debt — the `maxUpfrontFee` guard is quoted accordingly. Lower rates increase redemption risk (redemptions hit lowest-rate troves first).\n\n**Liquity V2 family is a pooled CDP** — one branch per collateral, troves = sub-accounts (`troveId`, the sub-account id from user data), user-set adjustable interest rates, and a per-branch Stability Pool as the stable-token earn side. Deployments: `LIQUITY_V2` (Ethereum) + friendly forks (`USDAF`, `FELIX`, `NERITE`, `QUILL`, `ENOSYS_LOANS`, `SONETA`, `EBISU`) — all share this surface; per-fork parameters (min debt, rate bounds, gas compensation) come from metadata.\n\n**Standard lending actions cover the day-to-day ops** for a `<DEPLOYMENT>_<chainId>_<COLL_INDEX>` market key:\n- stable-token deposit/withdraw → Stability Pool (earn; `isAll` withdraw uses the protocol sentinel)\n- collateral deposit/withdraw → `addColl`/`withdrawColl` (pass `troveId`)\n- borrow → `withdrawBold` (pass `troveId`; upfront-fee guard quoted automatically, override with `maxUpfrontFee`)\n- repay → `repayBold` clamped to `entireDebt − minDebt`; `isAll=true` routes to `closeTrove` (burns the full live debt from the wallet, returns collateral + the WETH gas compensation)\n\nThe endpoints in this section handle the trove lifecycle ops that need more than one amount or no amount at all. Fees to know: the upfront borrowing fee (≈7 days of average branch interest on any debt increase) and the premature rate-adjustment fee (same formula on the WHOLE debt when changing the rate within the cooldown). Repaying itself is always free.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["chainId","lender","troveId","interestRate"],"properties":{"chainId":{"type":"string"},"lender":{"type":"string","example":"LIQUITY_V2_1_0"},"troveId":{"type":"string"},"interestRate":{"type":"string","description":"New annual rate, WAD"},"maxUpfrontFee":{"type":"string","description":"Optional fee-guard override"}}}}}},"responses":{"200":{"200":{"description":"Rate-adjust transaction","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}}},"400":{"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"/v1/actions/liquity/claim-surplus":{"get":{"tags":["Actions › Liquity"],"summary":"Claim liquidation collateral surplus","operationId":"liquity-claim-surplus","description":"Claim the caller's post-liquidation collateral surplus on a branch (collateral above the liquidation penalty is escrowed in the CollSurplusPool).","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain id"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"Per-branch key"}],"responses":{"200":{"200":{"description":"Claim transaction","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}}},"400":{"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"/v1/actions/liquity/adjust-zombie":{"post":{"tags":["Actions › Liquity"],"summary":"Re-adjust a zombie trove","operationId":"liquity-adjust-zombie","description":"Bring a ZOMBIE trove (redeemed below the deployment minDebt — status 4, removed from the rate-ordered list) back above the floor. `adjustZombieTrove` is the only adjustment entry for zombie troves; it re-inserts at the trove's current rate. Collateral top-ups add the collateral approval; debt increases charge the upfront fee.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["chainId","lender","troveId"],"properties":{"chainId":{"type":"string"},"lender":{"type":"string"},"troveId":{"type":"string"},"collChange":{"type":"string","description":"Collateral delta, raw units"},"isCollIncrease":{"type":"boolean"},"boldChange":{"type":"string","description":"Stable-debt delta, raw units"},"isDebtIncrease":{"type":"boolean"},"maxUpfrontFee":{"type":"string"}}}}}},"responses":{"200":{"200":{"description":"Zombie-adjust transaction + approvals","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}}},"400":{"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"/v1/actions/river/open":{"post":{"tags":["Actions › River"],"summary":"Open a trove","operationId":"river-open","description":"Open a trove on a River market: deposit collateral, mint satUSD.\n\n**River (rebranded Satoshi Protocol) is a Prisma-lineage pooled CDP** minting satUSD behind one SatoshiXApp diamond per chain (BNB, Base, Hemi). One TroveManager per collateral = one `RIVER_<chainId>_<TM_INDEX>` market; troves are keyed by OWNER ADDRESS — at most one per user per market, no ids. Interest is protocol-set (currently 0%); the only borrowing cost is a one-off decaying-baseRate mint fee (0.5%–5%) on open/borrow-more, guarded by `maxFeePercentage` (quoted automatically).\n\n**Standard lending actions cover the day-to-day ops:**\n- satUSD deposit/withdraw → the single per-chain Stability Pool (attached to market index 0; zero approvals)\n- collateral deposit/withdraw → `addColl`/`withdrawColl` (no troveId — address-keyed)\n- borrow → `withdrawDebt` (mint fee applies)\n- repay → `repayDebt` clamped to `entireDebt − gasComp − minNetDebt`; `isAll=true` routes to `closeTrove` (burns `entireDebt − gasComp` from the wallet, returns all collateral; blocked in Recovery Mode)\n\nThe 2-satUSD debt-side gas compensation is minted on open and burned from the GasPool on close — it never touches the wallet.\n\nValidates the diamond-level minNetDebt and rejects paused/sunsetting markets; quotes the mint-fee guard live. Returns the transaction plus the collateral approval (to the diamond).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["chainId","lender","account","collAmount","amount"],"properties":{"chainId":{"type":"string","example":"8453"},"lender":{"type":"string","description":"Per-market key, e.g. RIVER_8453_0","example":"RIVER_8453_0"},"account":{"type":"string","description":"Trove owner (tx sender)"},"collAmount":{"type":"string","description":"Collateral amount, raw units"},"amount":{"type":"string","description":"satUSD debt to mint, raw units (net ≥ minNetDebt)"},"maxFeePercentage":{"type":"string","description":"Optional mint-fee guard override, WAD percentage"}}}}}},"responses":{"200":{"200":{"description":"Open-trove transaction + approval","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}}},"400":{"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"/v1/actions/river/close":{"get":{"tags":["Actions › River"],"summary":"Close the trove","operationId":"river-close","description":"Close the caller's trove on a market: burns `entireDebt − gasCompensation` satUSD from the wallet, returns all collateral. Blocked in Recovery Mode (global TCR < 150%). Equivalent to `lending/repay` with `isAll=true`.\n\n**River (rebranded Satoshi Protocol) is a Prisma-lineage pooled CDP** minting satUSD behind one SatoshiXApp diamond per chain (BNB, Base, Hemi). One TroveManager per collateral = one `RIVER_<chainId>_<TM_INDEX>` market; troves are keyed by OWNER ADDRESS — at most one per user per market, no ids. Interest is protocol-set (currently 0%); the only borrowing cost is a one-off decaying-baseRate mint fee (0.5%–5%) on open/borrow-more, guarded by `maxFeePercentage` (quoted automatically).\n\n**Standard lending actions cover the day-to-day ops:**\n- satUSD deposit/withdraw → the single per-chain Stability Pool (attached to market index 0; zero approvals)\n- collateral deposit/withdraw → `addColl`/`withdrawColl` (no troveId — address-keyed)\n- borrow → `withdrawDebt` (mint fee applies)\n- repay → `repayDebt` clamped to `entireDebt − gasComp − minNetDebt`; `isAll=true` routes to `closeTrove` (burns `entireDebt − gasComp` from the wallet, returns all collateral; blocked in Recovery Mode)\n\nThe 2-satUSD debt-side gas compensation is minted on open and burned from the GasPool on close — it never touches the wallet.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain id"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"Per-market key, e.g. RIVER_8453_0"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"Trove owner"}],"responses":{"200":{"200":{"description":"Close-trove transaction","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}}},"400":{"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"/v1/actions/river/claim-surplus":{"get":{"tags":["Actions › River"],"summary":"Claim collateral surplus","operationId":"river-claim-surplus","description":"Claim the caller's post-liquidation/redemption collateral surplus on a market (`TroveManager.claimCollateral`). Surplus balances are visible in user data (`riverInfo.collateralSurplus`).","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain id"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"Per-market key"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"Recipient (the surplus owner)"}],"responses":{"200":{"200":{"description":"Claim transaction","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}}},"400":{"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"/v1/actions/twyne/create-position":{"get":{"tags":["Actions › Twyne"],"summary":"Create a position (deploy a collateral vault)","operationId":"twyne-create-position","description":"Deploy the borrower's collateral vault, choosing its liquidation LTV. **This is the first step on Twyne** — there is no \"first deposit opens it\" path, and the standard lending ops refuse an account that has no vault in the market.\n\n**Twyne is a credit-delegation layer over Aave V3 and Euler V2**, not a lending market of its own: capital never leaves the external protocol. Credit-LPs deposit a receipt token they already hold (`eWETH`, or a Twyne wrapper around an aToken) into an intermediate **credit vault**; a borrower deploys a **collateral vault** — one contract per position — that RESERVES that credit, so the external market sees one ordinary borrower while the user's own collateral carries a much higher LTV (93.26 % → 98 %).\n\nThree consequences shape this API:\n\n- **A position is a contract, and there may be SEVERAL per user per market.** Every op past the create takes an optional `posId` (the collateral vault address); omitted, it resolves the account's only vault and refuses when there is more than one. Discovery is `factory.getCollateralVaults(borrower)`.\n- **There is no per-market LTV.** Each position carries its own `twyneLiqLTV`, chosen at deploy time inside a live BAND: `extLiqLTV × externalLiqBuffer ≤ chosen ≤ maxTwyneLTVs`. Both bounds move — governance ramps the ceiling, and for a PT market the floor drifts as the collateral ages — so they are read fresh on every call and returned as `data.band`.\n- **The collateral is not all the borrower's.** `totalAssetsDepositedOrReserved − maxRelease()` is what a withdraw can pay out; the rest is reserved Credit-LP capital.\n\nEvery borrower op is an `evc.call` through **Twyne's OWN EVC** `0xef39D649…`, never Euler's canonical one. Liquidation is by INHERITANCE — the liquidator becomes the borrower and heals the position — so there is no seize-and-sell penalty auction.\n\n`liqLtv` is validated against a LIVE band read and is refused above the ceiling, below the floor, or against collateral that has already MATURED (two of the three PT markets are expired — existing positions there stay actionable, new ones are not opened).\n\n**The vault's address is not returned**: it is only knowable once the deploy has executed, which the response states as `data.vaultAddressKnownAfterExecution`. Read it from the `T_CollateralVaultCreated` event in the receipt, or from `factory.getCollateralVaults(account)` afterwards. A single batch that creates AND funds the position needs the address up front — that is `evc.batchSimulation`, not a pinned CREATE2 hash, because the hash is a property of the factory implementation and that has already changed four times.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain id"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"Per-market key, e.g. TWYNE_1_<INTERMEDIATE_VAULT>_<TARGET_ASSET>"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"Borrower (tx sender)"},{"name":"liqLtv","in":"query","required":true,"schema":{"type":"string"},"description":"The position's liquidation LTV, 1e4 scale (e.g. 9500 = 95 %). Must sit inside the market's live band, returned as data.band"}],"responses":{"200":{"200":{"description":"Create-position transaction + the validated band","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}}},"400":{"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"/v1/actions/twyne/set-liq-ltv":{"get":{"tags":["Actions › Twyne"],"summary":"Move a position's liquidation LTV","operationId":"twyne-set-liq-ltv","description":"Change an existing position's `twyneLiqLTV` — the borrower's risk dial, and the cheapest deleverage lever the protocol has.\n\n**Twyne is a credit-delegation layer over Aave V3 and Euler V2**, not a lending market of its own: capital never leaves the external protocol. Credit-LPs deposit a receipt token they already hold (`eWETH`, or a Twyne wrapper around an aToken) into an intermediate **credit vault**; a borrower deploys a **collateral vault** — one contract per position — that RESERVES that credit, so the external market sees one ordinary borrower while the user's own collateral carries a much higher LTV (93.26 % → 98 %).\n\nThree consequences shape this API:\n\n- **A position is a contract, and there may be SEVERAL per user per market.** Every op past the create takes an optional `posId` (the collateral vault address); omitted, it resolves the account's only vault and refuses when there is more than one. Discovery is `factory.getCollateralVaults(borrower)`.\n- **There is no per-market LTV.** Each position carries its own `twyneLiqLTV`, chosen at deploy time inside a live BAND: `extLiqLTV × externalLiqBuffer ≤ chosen ≤ maxTwyneLTVs`. Both bounds move — governance ramps the ceiling, and for a PT market the floor drifts as the collateral ages — so they are read fresh on every call and returned as `data.band`.\n- **The collateral is not all the borrower's.** `totalAssetsDepositedOrReserved − maxRelease()` is what a withdraw can pay out; the rest is reserved Credit-LP capital.\n\nEvery borrower op is an `evc.call` through **Twyne's OWN EVC** `0xef39D649…`, never Euler's canonical one. Liquidation is by INHERITANCE — the liquidator becomes the borrower and heals the position — so there is no seize-and-sell penalty auction.\n\n**Raising it RESERVES more Credit-LP capital** (raising leverage and what the borrower pays), and can therefore fail on the credit vault's liquidity or supply cap even when the value itself is legal — that case is refused up front, with the reason, rather than left to revert. **Lowering it RELEASES credit** and is always possible.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain id"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"Per-market key"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"Borrower (tx sender)"},{"name":"liqLtv","in":"query","required":true,"schema":{"type":"string"},"description":"Target liquidation LTV, 1e4 scale"},{"name":"posId","in":"query","schema":{"type":"string"},"description":"Collateral vault address. Optional when the account holds exactly one vault in this market"}],"responses":{"200":{"200":{"description":"setTwyneLiqLTV transaction + the band, including the previous value","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}}},"400":{"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"/v1/actions/twyne/rebalance":{"get":{"tags":["Actions › Twyne"],"summary":"Release over-reserved credit","operationId":"twyne-rebalance","description":"Release the excess credit that interest accrual has left reserved on a position, lowering what the borrower pays.\n\n**Twyne is a credit-delegation layer over Aave V3 and Euler V2**, not a lending market of its own: capital never leaves the external protocol. Credit-LPs deposit a receipt token they already hold (`eWETH`, or a Twyne wrapper around an aToken) into an intermediate **credit vault**; a borrower deploys a **collateral vault** — one contract per position — that RESERVES that credit, so the external market sees one ordinary borrower while the user's own collateral carries a much higher LTV (93.26 % → 98 %).\n\nThree consequences shape this API:\n\n- **A position is a contract, and there may be SEVERAL per user per market.** Every op past the create takes an optional `posId` (the collateral vault address); omitted, it resolves the account's only vault and refuses when there is more than one. Discovery is `factory.getCollateralVaults(borrower)`.\n- **There is no per-market LTV.** Each position carries its own `twyneLiqLTV`, chosen at deploy time inside a live BAND: `extLiqLTV × externalLiqBuffer ≤ chosen ≤ maxTwyneLTVs`. Both bounds move — governance ramps the ceiling, and for a PT market the floor drifts as the collateral ages — so they are read fresh on every call and returned as `data.band`.\n- **The collateral is not all the borrower's.** `totalAssetsDepositedOrReserved − maxRelease()` is what a withdraw can pay out; the rest is reserved Credit-LP capital.\n\nEvery borrower op is an `evc.call` through **Twyne's OWN EVC** `0xef39D649…`, never Euler's canonical one. Liquidation is by INHERITANCE — the liquidator becomes the borrower and heals the position — so there is no seize-and-sell penalty auction.\n\n**`rebalance()` is PERMISSIONLESS** — the transaction goes straight to the collateral vault, so a keeper may send it for someone else; `operator` names WHOSE position, not who must sign. The amount that will be released is returned as `data.releasableCredit`.\n\nA position with nothing to release is refused before it becomes a transaction: the vault's `canRebalance()` REVERTS in that state, so the call would revert with no usable reason.","parameters":[{"name":"chainId","in":"query","required":true,"schema":{"type":"string"},"description":"Chain id"},{"name":"lender","in":"query","required":true,"schema":{"type":"string"},"description":"Per-market key"},{"name":"operator","in":"query","required":true,"schema":{"type":"string"},"description":"The position owner (NOT necessarily the sender — the op is permissionless)"},{"name":"posId","in":"query","schema":{"type":"string"},"description":"Collateral vault address. Optional when the account holds exactly one vault in this market"}],"responses":{"200":{"200":{"description":"Rebalance transaction + releasableCredit","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"additionalProperties":true,"description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}}},"400":{"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"/v1/actions/allocate":{"post":{"tags":["Actions › Allocate"],"summary":"Allocate (multi-op batch)","description":"Bundles multiple lending and token operations into a single composer transaction. Useful for portfolio rebalances, atomic deposit-then-borrow flows, or wrap/unwrap + deposit combos that should not be split across separate user signatures.\n\n**Request body:** `{ chainId, operator, actions[] }`. Each `actions[]` entry is `{ type, params }` where `type` is one of `Deposit | Withdraw | Borrow | Repay | Transfer | Wrap | Unwrap | Sweep`. The `params` shape is action-specific (asset, amount, market, etc.) — see the `AllocationAction` schema.\n\n**Response:** a single composer-call payload (`operations` / `data` / `value`) ready to send to the composer contract, plus `permissionTxns` for any required ERC20 approvals or borrow delegations.\n\nReturns 422 with field-level details if validation fails (unknown action type, missing required params, incompatible chain/lender combo).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllocateRequest"}}}},"responses":{"200":{"description":"Allocation transaction","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","enum":[true]},"data":{"nullable":true,"$ref":"#/components/schemas/AllocateResponse","description":"Informational data (quotes, simulation results, etc.)"},"actions":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/ActionSet"}],"description":"Transaction calldata and approvals. Null for quote-only responses (no account provided)."}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation failed (e.g. unknown action type, missing required params)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"components":{"schemas":{"ApiError":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable error code","example":"MISSING_PARAM"},"message":{"type":"string","description":"Human-readable error message","example":"Missing required parameter: chainId"},"details":{"description":"Additional error context (varies by error code)"}}},"ErrorEnvelope":{"type":"object","required":["success","error"],"properties":{"success":{"type":"boolean","enum":[false]},"error":{"$ref":"#/components/schemas/ApiError"}},"description":"Standard error response envelope."},"ActionSet":{"type":"object","required":["transactions"],"properties":{"transactions":{"type":"array","items":{"$ref":"#/components/schemas/TransactionRequest"},"description":"Pre-trade setup transactions (e.g. e-mode switch, collateral enable). Execute these before the main swap. Empty when no setup is needed."},"alternatives":{"type":"array","items":{"$ref":"#/components/schemas/TransactionRequest"},"description":"DEX aggregator swap transactions sorted by best output (descending). Each entry's `description` is the aggregator name. The client should pick one to execute. Present on loop action endpoints."},"permissions":{"type":"array","nullable":true,"items":{"$ref":"#/components/schemas/PermissionTransaction"},"description":"Approval/delegation transactions that must execute before both `transactions` and `alternatives`. Includes ERC20 allowances (targeting the composer contract) and lender borrow/withdrawal delegations (targeting the lending protocol contract directly). Filtered against on-chain state so only missing approvals are returned. Null when no approvals are needed."}},"description":"Transaction calldata and required approvals returned by action endpoints."},"TransactionRequest":{"type":"object","required":["to","data","value"],"properties":{"to":{"type":"string","description":"Target contract address","example":"0x1234...abcd"},"data":{"type":"string","description":"Encoded calldata","example":"0xabcdef..."},"value":{"type":"string","description":"ETH value to send with the transaction","example":"0"},"description":{"type":"string","description":"Human-readable label. For `alternatives`, this is the aggregator name (e.g. \"Paraswap\"). For `transactions`, describes the setup action (e.g. \"Switch e-mode to 1\")."}}},"PermissionTransaction":{"type":"object","required":["to","data","value"],"properties":{"to":{"type":"string","description":"Target contract address"},"data":{"type":"string","description":"Encoded calldata"},"value":{"type":"string","description":"ETH value","example":"0"},"description":{"type":"string","description":"Human-readable description of the approval (e.g. \"Approve borrow for AAVE_V3\", \"Approve ERC20\")"},"spender":{"type":"string","description":"ERC-20 approve spender (x-chain permissions). Match it against the selected quote's `approvalTarget` — several bridges can share one spender, so do not match by description."}}},"RateMetric":{"type":"object","description":"A current/projected pair for a single rate metric.","required":["current","projected"],"properties":{"current":{"type":"number","description":"Current value"},"projected":{"type":"number","description":"Projected value after the action"}}},"RateImpactItem":{"type":"object","description":"Projected interest-rate impact on a single market. Each item is keyed by `marketUid` so the client knows which market the rates refer to.","required":["marketUid","utilization","borrowRate","depositRate"],"properties":{"marketUid":{"type":"string","description":"Market identifier (format: `lender:chainId:address`)","example":"AAVE_V3:8453:0x4200000000000000000000000000000000000006"},"utilization":{"$ref":"#/components/schemas/RateMetric","description":"Utilization ratio (0–1)","example":{"current":0.85,"projected":0.83}},"borrowRate":{"$ref":"#/components/schemas/RateMetric","description":"Borrow APR (%)","example":{"current":3.2,"projected":3}},"depositRate":{"$ref":"#/components/schemas/RateMetric","description":"Deposit/supply APR (%)","example":{"current":2.1,"projected":1.9}}}},"LendingRawResponse":{"type":"object","description":"Response from direct-mode lending operations (deposit, withdraw, borrow, repay).","required":["transaction","permissionTxns"],"properties":{"transaction":{"$ref":"#/components/schemas/TransactionRequest"},"permissionTxns":{"type":"array","description":"Approval transactions that must be executed before the main transaction. Empty when the user already has sufficient allowances.","items":{"$ref":"#/components/schemas/PermissionTransaction"}},"rateImpact":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market. Single-market actions produce 1 entry; loop actions produce 2. Null if IRM data is unavailable."}}},"LendingProxyResponse":{"type":"object","description":"Response from proxy-mode lending operations (via 1delta composer).","required":["transaction","permissionTxns"],"properties":{"transaction":{"$ref":"#/components/schemas/TransactionRequest"},"permissionTxns":{"type":"array","description":"Approval transactions that must be executed before the main transaction. Empty when the user already has sufficient allowances.","items":{"$ref":"#/components/schemas/PermissionTransaction"}},"rateImpact":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market. Single-market actions produce 1 entry; loop actions produce 2. Null if IRM data is unavailable."}}},"SimulationBody":{"type":"object","description":"Request body for POST variants of action endpoints. Provides the current portfolio state so the API can project post-trade metrics (health factor, borrow capacity, APRs) before and after the trade.\n\n**Data source:** All fields come directly from the `/v1/data/lending/user-positions` endpoint. For a given lender + sub-account, use:\n\n```\nGET /v1/data/lending/user-positions?account=0x...&chains=1\n\n// response.data.items[] is an array of lender entries.\n// Pick the entry matching your target lender + chain:\nconst lenderEntry = response.data.items.find(e => e.lender === 'AAVE_V3' && e.chainId === '1')\n\n// Each lenderEntry.data[] contains sub-accounts (usually one, index 0):\nconst subAccount = lenderEntry.data[0]\n\n// POST body for any action endpoint:\n{\n  \"balanceData\": subAccount.balanceData,\n  \"aprData\":     subAccount.aprData,\n  \"positions\":   subAccount.positions,\n  \"modeId\":      subAccount.userConfig.selectedMode  // optional\n}\n```\n\nIf the body is omitted, the API fetches balances on-chain automatically (slower, no simulation in response).","required":["balanceData","aprData"],"properties":{"balanceData":{"$ref":"#/components/schemas/BalanceData"},"aprData":{"$ref":"#/components/schemas/AprData"},"modeId":{"type":"string","description":"Mode/config key from `userConfig.selectedMode` (defaults to \"0\")","example":"0"},"positions":{"type":"array","items":{"$ref":"#/components/schemas/SimulationPosition"},"description":"Current lending positions from the matching sub-account's `positions` array. The full `LendingPosition` objects returned by user-positions are accepted — only the fields in `SimulationPosition` are used. Always include this for accurate health-factor and borrow-capacity projections."}}},"SimulationPosition":{"type":"object","description":"A single lending position as returned by user-data endpoints. Pass positions through as-is — the API enriches them internally with protocol-specific metadata.","required":["marketUid","depositsUSD","debtUSD","debtStableUSD","collateralEnabled"],"properties":{"marketUid":{"type":"string","description":"Unique market identifier (format: `{lender}:{chainId}:{address}`)","example":"AAVE_V3:1:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"},"depositsUSD":{"type":"number","description":"Deposit amount in USD","example":5000},"debtUSD":{"type":"number","description":"Variable debt in USD","example":2000},"debtStableUSD":{"type":"number","description":"Stable debt in USD","example":0},"collateralEnabled":{"type":"boolean","description":"Whether this asset is enabled as collateral","example":true}}},"SimulationPreState":{"type":"object","description":"Portfolio state before the trade.","properties":{"healthFactor":{"type":"number","description":"Health factor before the trade (null-safe: capped at 1e18 when no debt)","example":1.85},"borrowCapacity":{"type":"number","description":"Borrow capacity (USD) before the trade","example":3000}}},"SimulationPostState":{"type":"object","description":"Projected portfolio state after the trade.","properties":{"healthFactor":{"type":"number","description":"Projected health factor after the trade","example":2.1},"borrowCapacity":{"type":"number","description":"Projected borrow capacity (USD) after the trade","example":3500},"balanceData":{"$ref":"#/components/schemas/BalanceData"},"aprData":{"$ref":"#/components/schemas/AprData"}}},"PostTradeMetrics":{"type":"object","description":"Projected portfolio metrics after a single-asset lending action (deposit, withdraw, borrow, repay). Pre-trade state is provided for comparison.","properties":{"pre":{"$ref":"#/components/schemas/SimulationPreState"},"post":{"$ref":"#/components/schemas/SimulationPostState"}}},"LoopPostTradeMetrics":{"type":"object","description":"Projected portfolio metrics after a multi-asset loop action (leverage, close, collateral-swap, debt-swap). Pre-trade state is provided for comparison.","properties":{"pre":{"$ref":"#/components/schemas/SimulationPreState"},"post":{"$ref":"#/components/schemas/SimulationPostState"}}},"LendingRawSimulationResponse":{"type":"object","description":"Direct-mode lending response enriched with post-trade simulation. Returned on POST (with body) or GET with `simulate=true`.","required":["transaction","permissionTxns"],"properties":{"transaction":{"$ref":"#/components/schemas/TransactionRequest"},"permissionTxns":{"type":"array","items":{"$ref":"#/components/schemas/PermissionTransaction"}},"rateImpact":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market. Single-market actions produce 1 entry; loop actions produce 2. Null if IRM data is unavailable."},"simulation":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/PostTradeMetrics"}],"description":"Projected post-trade metrics, or null if simulation failed"},"simulationError":{"type":"string","description":"Error message if simulation failed"}}},"LendingProxySimulationResponse":{"type":"object","description":"Proxy-mode lending response enriched with post-trade simulation. Returned on POST (with body) or GET with `simulate=true`.","required":["transaction","permissionTxns"],"properties":{"transaction":{"$ref":"#/components/schemas/TransactionRequest"},"permissionTxns":{"type":"array","items":{"$ref":"#/components/schemas/PermissionTransaction"}},"rateImpact":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market. Single-market actions produce 1 entry; loop actions produce 2. Null if IRM data is unavailable."},"simulation":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/PostTradeMetrics"}],"description":"Projected post-trade metrics, or null if simulation failed"},"simulationError":{"type":"string","description":"Error message if simulation failed"}}},"RangeResult":{"allOf":[{"$ref":"#/components/schemas/LeveragePair"}],"type":"object","description":"Leverage pair metadata enriched with the computed max open amount. Inherits all fields from LeveragePair.","required":["amountIn","amountOut","amountUSD"],"properties":{"amountIn":{"type":"number","description":"Max open amount in the short (debt) asset units","example":1.5},"amountOut":{"type":"number","description":"Max open amount in the long (collateral) asset units","example":1.2},"amountUSD":{"type":"number","description":"Max open amount in USD","example":3000},"payAmountUSD":{"type":"number","description":"USD value of the zap deposit (only present when payAmount query param is provided)","example":2300},"modeAnalysis":{"type":"object","description":"E-mode switching analysis (only present in multi-pair mode)","properties":{"userMode":{"type":"string","description":"User's current e-mode ID","example":"0"},"targetMode":{"type":"string","description":"Pair's optimal e-mode ID","example":"2"},"canSwitchToTargetMode":{"type":"boolean","description":"Whether user can switch to the target mode without conflicting positions"},"userModeRange":{"$ref":"#/components/schemas/RangeAmounts","description":"Range in the user's current mode (null if pair disabled in that mode)"},"targetModeRange":{"$ref":"#/components/schemas/RangeAmounts","description":"Range in the pair's optimal mode"}}}}},"RangeAmounts":{"type":"object","description":"Amount triplet returned by all range endpoints.","required":["amountIn","amountOut","amountUSD"],"properties":{"amountIn":{"type":"number","description":"Amount in the input asset units","example":1.5},"amountOut":{"type":"number","description":"Amount in the output asset units","example":1.2},"amountUSD":{"type":"number","description":"Amount in USD","example":3000}}},"CollateralSwapRangeResult":{"allOf":[{"$ref":"#/components/schemas/LeveragePair"}],"type":"object","description":"Max swappable collateral. amountUSD = depositsUSD of the source collateral position.","required":["amountIn","amountOut","amountUSD"],"properties":{"amountIn":{"type":"number","description":"Max amount of source collateral to withdraw (in asset units)","example":5000},"amountOut":{"type":"number","description":"Equivalent amount in the target collateral asset units","example":5000},"amountUSD":{"type":"number","description":"Max swappable amount in USD","example":5000}}},"DebtSwapRangeResult":{"allOf":[{"$ref":"#/components/schemas/LeveragePair"}],"type":"object","description":"Max swappable debt. amountUSD = debtUSD + debtStableUSD of the source debt position.","required":["amountIn","amountOut","amountUSD","denomination"],"properties":{"amountIn":{"type":"number","description":"Max amount of source debt to repay (in asset units)","example":4},"amountOut":{"type":"number","description":"Equivalent amount in the target debt asset units","example":3.5},"amountUSD":{"type":"number","description":"Max swappable amount in USD","example":8000},"denomination":{"type":"string","enum":["exactInput","exactOutput"],"description":"Which side is the base denomination"}}},"CloseRangeResult":{"allOf":[{"$ref":"#/components/schemas/LeveragePair"}],"type":"object","description":"Max closeable amount. Bounded by min(collateral, debt).","required":["amountIn","amountOut","amountUSD","denomination"],"properties":{"amountIn":{"type":"number","description":"Max collateral to withdraw (in long asset units)","example":3.5},"amountOut":{"type":"number","description":"Max debt to repay (in short asset units)","example":4},"amountUSD":{"type":"number","description":"Max closeable amount in USD (min of collateral and debt)","example":8000},"denomination":{"type":"string","enum":["exactInput","exactOutput"],"description":"Which side is the base denomination"}}},"MarginSimulationQuoteResponse":{"type":"object","description":"Quote-only loop response enriched with post-trade simulation (POST only).","required":["lender","quotes"],"properties":{"lender":{"type":"string"},"quotes":{"type":"array","items":{"type":"object","properties":{"deltas":{"$ref":"#/components/schemas/QuoteDelta"},"rateImpact":{"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market for THIS quote (its own trade amounts). Omitted if IRM data is unavailable."}}}},"rateImpact":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market. Single-market actions produce 1 entry; loop actions produce 2. Null if IRM data is unavailable."},"simulation":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/LoopPostTradeMetrics"}],"description":"Projected post-trade metrics, or null if simulation failed"},"simulationError":{"type":"string","description":"Error message if simulation failed"}}},"MarginSimulationBuildResponse":{"type":"object","description":"Full build loop response enriched with post-trade simulation (POST only).","required":["lender","quotes","permissionTxns"],"properties":{"lender":{"type":"string"},"quotes":{"type":"array","items":{"type":"object","properties":{"deltas":{"$ref":"#/components/schemas/QuoteDelta"},"tx":{"$ref":"#/components/schemas/TransactionRequest"},"rateImpact":{"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market for THIS quote (its own trade amounts). Omitted if IRM data is unavailable."}}}},"permissionTxns":{"type":"array","items":{"$ref":"#/components/schemas/PermissionTransaction"}},"rateImpact":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market. Single-market actions produce 1 entry; loop actions produce 2. Null if IRM data is unavailable."},"simulation":{"nullable":true,"oneOf":[{"$ref":"#/components/schemas/LoopPostTradeMetrics"}],"description":"Projected post-trade metrics, or null if simulation failed"},"simulationError":{"type":"string","description":"Error message if simulation failed"}}},"QuoteDelta":{"type":"object","properties":{"aggregator":{"type":"string","description":"Aggregator source"},"tradeInput":{"type":"number","description":"Trade input amount"},"tradeOutput":{"type":"number","description":"Trade output amount"},"deltas":{"type":"object","additionalProperties":true,"description":"Balance deltas"}}},"MarginQuoteResponse":{"type":"object","description":"Quote-only response (no account provided). No transaction is built.","required":["lender","quotes"],"properties":{"lender":{"type":"string","description":"Protocol identifier"},"quotes":{"type":"array","items":{"type":"object","properties":{"deltas":{"$ref":"#/components/schemas/QuoteDelta"},"rateImpact":{"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market for THIS quote (its own trade amounts). Omitted if IRM data is unavailable."}}}},"rateImpact":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market. Single-market actions produce 1 entry; loop actions produce 2. Null if IRM data is unavailable."}}},"MarginBuildResponse":{"type":"object","description":"Full build response (account provided). Includes transaction calldata.","required":["lender","quotes","permissionTxns"],"properties":{"lender":{"type":"string","description":"Protocol identifier"},"quotes":{"type":"array","items":{"type":"object","properties":{"deltas":{"$ref":"#/components/schemas/QuoteDelta"},"tx":{"$ref":"#/components/schemas/TransactionRequest"},"rateImpact":{"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market for THIS quote (its own trade amounts). Omitted if IRM data is unavailable."}}}},"permissionTxns":{"type":"array","items":{"$ref":"#/components/schemas/PermissionTransaction"}},"rateImpact":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/RateImpactItem"},"description":"Projected interest-rate impact per market. Single-market actions produce 1 entry; loop actions produce 2. Null if IRM data is unavailable."}}},"SpotQuoteResponse":{"type":"object","description":"Quote-only response for spot swap (no account provided).","required":["currencyIn","currencyOut","quotes"],"properties":{"currencyIn":{"type":"object","additionalProperties":true,"description":"Input currency info"},"currencyOut":{"type":"object","additionalProperties":true,"description":"Output currency info"},"quotes":{"type":"array","items":{"type":"object","properties":{"aggregator":{"type":"string"},"tradeInput":{"type":"number"},"tradeOutput":{"type":"number"}}}}}},"SpotBuildResponse":{"type":"object","description":"Full build response for spot swap (account provided).","required":["currencyIn","currencyOut","quotes","permissionTxns"],"properties":{"currencyIn":{"type":"object","additionalProperties":true,"description":"Input currency info"},"currencyOut":{"type":"object","additionalProperties":true,"description":"Output currency info"},"quotes":{"type":"array","items":{"type":"object","properties":{"aggregator":{"type":"string"},"tradeInput":{"type":"number"},"tradeOutput":{"type":"number"},"tx":{"$ref":"#/components/schemas/TransactionRequest"}}}},"permissionTxns":{"type":"array","items":{"$ref":"#/components/schemas/PermissionTransaction"}}}},"XChainQuoteResponse":{"type":"object","description":"Quote-only response for cross-chain swap (no account provided).","required":["currencyIn","currencyOut","quotes"],"properties":{"currencyIn":{"type":"object","additionalProperties":true,"description":"Input currency info (source chain)"},"currencyOut":{"type":"object","additionalProperties":true,"description":"Output currency info (destination chain)"},"quotes":{"type":"array","items":{"type":"object","properties":{"bridge":{"type":"string"},"tradeInput":{"type":"number"},"tradeOutput":{"type":"number"},"estimatedDuration":{"type":"number","description":"Estimated bridging duration in seconds"}}}}}},"XChainBuildResponse":{"type":"object","description":"Full build response for cross-chain swap (account provided).","required":["currencyIn","currencyOut","quotes","permissionTxns"],"properties":{"currencyIn":{"type":"object","additionalProperties":true,"description":"Input currency info (source chain)"},"currencyOut":{"type":"object","additionalProperties":true,"description":"Output currency info (destination chain)"},"quotes":{"type":"array","items":{"type":"object","properties":{"bridge":{"type":"string"},"tradeInput":{"type":"number"},"tradeOutput":{"type":"number"},"estimatedDuration":{"type":"number","description":"Estimated bridging duration in seconds"},"approvalTarget":{"type":"string","description":"This bridge's deposit contract — the ERC-20 approve spender"},"approvalRequired":{"type":"boolean","description":"False when the existing on-chain allowance already covers the input amount"},"tx":{"$ref":"#/components/schemas/TransactionRequest"}}}},"permissionTxns":{"type":"array","description":"ERC-20 approves per bridge deposit contract; each is labeled with the bridge name — execute only the one matching the chosen quote","items":{"$ref":"#/components/schemas/PermissionTransaction"}}}},"AllocationOperation":{"type":"string","enum":["Deposit","Withdraw","Borrow","Repay","Transfer","Wrap","Unwrap","Sweep"]},"AllocationAction":{"type":"object","required":["type","params"],"properties":{"type":{"$ref":"#/components/schemas/AllocationOperation"},"params":{"type":"object","additionalProperties":true}}},"AllocateRequest":{"type":"object","required":["chainId","operator","actions"],"properties":{"chainId":{"type":"string","example":"1"},"operator":{"type":"string","example":"0xOperatorAddress"},"actions":{"type":"array","items":{"$ref":"#/components/schemas/AllocationAction"}}}},"AllocateResponse":{"type":"object","required":["operations","data","value","permissionTxns"],"properties":{"operations":{"type":"string"},"data":{"type":"string"},"value":{"type":"string"},"permissionTxns":{"type":"array","items":{"$ref":"#/components/schemas/PermissionTransaction"}}}},"HealthResponse":{"type":"object","properties":{"latestHour":{"type":"string","format":"date-time","nullable":true,"description":"Latest data generation hour"}}},"ChainsResponse":{"type":"object","properties":{"count":{"type":"integer","description":"Number of supported chains"},"items":{"type":"array","description":"Supported chains, sorted ascending by numeric chainId.","items":{"type":"object","required":["chainId","name","logoURI"],"properties":{"chainId":{"type":"string","description":"Decimal EVM chain id, as a string.","example":"1"},"name":{"type":"string","description":"Human-readable display name. Prefers the registry `shortName` (concise, e.g. \"eth\"); falls back to the long `name`, then to \"Chain {id}\".","example":"eth"},"logoURI":{"type":"string","format":"uri","description":"Absolute URL to the chain icon. Always populated; consumers should handle broken images gracefully.","example":"https://raw.githubusercontent.com/1delta-DAO/chains/main/1.webp"}}}}}},"RpcsResponse":{"type":"object","description":"Chain ID to array of RPC URLs. Keys are chain IDs; values are arrays of RPC endpoint URLs.","additionalProperties":{"type":"array","items":{"type":"string","description":"RPC endpoint URL"}}},"PriceResponse":{"type":"object","properties":{"asOf":{"type":"string","format":"date-time"},"count":{"type":"integer"},"items":{"type":"object","additionalProperties":{"type":"number"},"description":"Map of asset_group → priceUsd","example":{"USDC":1.0001,"WBTC":67432.12}},"debug":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}},"IntrinsicYieldResponse":{"type":"object","properties":{"asOf":{"type":"string","format":"date-time"},"count":{"type":"integer"},"intrinsicApr":{"type":"object","additionalProperties":{"type":"number"},"description":"Map of asset_group → APR in percent","example":{"USDC":3.25,"ETH":2.1}}}},"IntrinsicYieldSnapshotResponse":{"type":"object","properties":{"count":{"type":"integer"},"series":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/IntrinsicYieldPoint"}},"description":"Map of assetGroup → array of {dataTs, intrinsicYield}"}}},"IntrinsicYieldPoint":{"type":"object","properties":{"dataTs":{"type":"string","format":"date-time"},"intrinsicYield":{"type":"number"}}},"LenderYieldResponse":{"type":"object","properties":{"count":{"type":"integer"},"data":{"type":"object","additionalProperties":true}}},"LenderInfo":{"type":"object","description":"Protocol/lender metadata (name, logo).","properties":{"key":{"type":"string","description":"Lender key identifier","example":"AAVE_V3"},"name":{"type":"string","nullable":true,"description":"Human-readable lender name","example":"Aave V3"},"logoURI":{"type":"string","nullable":true,"description":"Lender logo URL","example":"https://raw.githubusercontent.com/1delta-DAO/protocol-icons/main/lender/aave_v3.webp"}}},"MarketAssetInfo":{"type":"object","description":"Token metadata for an underlying asset.","properties":{"chainId":{"type":"string","example":"1"},"address":{"type":"string","description":"Token contract address (lowercase)","example":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"},"symbol":{"type":"string","example":"USDC"},"name":{"type":"string","example":"USD Coin"},"decimals":{"type":"integer","nullable":true,"example":6},"logoURI":{"type":"string","nullable":true},"assetGroup":{"type":"string","nullable":true,"description":"Canonical asset group (e.g. \"USDC\", \"ETH\", \"BTC\")","example":"USDC"},"currencyId":{"type":"string","nullable":true},"props":{"type":"object","nullable":true,"additionalProperties":true,"description":"Protocol-specific asset properties"}}},"MarketPriceInfo":{"type":"object","description":"Market prices for an asset with 24h comparison.","properties":{"priceUsd":{"type":"number","nullable":true,"description":"Current price in USD"},"priceTs":{"type":"string","format":"date-time","nullable":true,"description":"Timestamp of current price"},"priceUsd24h":{"type":"number","nullable":true,"description":"Price 24 hours ago in USD"},"priceTs24h":{"type":"string","format":"date-time","nullable":true,"description":"Timestamp of 24h-ago price"},"priceChange24h":{"type":"number","nullable":true,"description":"Percentage price change over 24h"}}},"MarketOraclePrice":{"type":"object","description":"On-chain oracle price data.","properties":{"oraclePrice":{"type":"number","nullable":true,"description":"Raw on-chain oracle price"},"oraclePriceUsd":{"type":"number","nullable":true,"description":"Oracle price denominated in USD"}}},"MarketUnderlyingInfo":{"type":"object","description":"Nested asset metadata, oracle prices, and market prices for a lending market.","properties":{"asset":{"$ref":"#/components/schemas/MarketAssetInfo"},"oraclePrice":{"$ref":"#/components/schemas/MarketOraclePrice"},"prices":{"$ref":"#/components/schemas/MarketPriceInfo"}}},"OracleFeed":{"type":"object","description":"Classification of one price-oracle feed backing a market, from the oracle-risk pipeline.","properties":{"asset":{"type":"string","nullable":true,"description":"Priced asset symbol (the reported numerator)."},"oracle":{"type":"string","nullable":true,"description":"Oracle / feed / adapter address (lowercased)."},"provider":{"type":"string","nullable":true,"description":"Oracle mechanism: `chainlink`, `redstone`, `pyth`, `chronicle`, `composite`, `price-cap` (Aave PriceCapAdapter), `exchange-rate` (correlated/LST, e.g. Venus OneJumpOracle), `pendle-pt`, `constant`, or a lender-specific stub (`compound-v2-oracle`, `dolomite-oracle`, `fluid-oracle`)."},"priceDescription":{"type":"string","nullable":true,"description":"Decoded reported pair, e.g. `\"WBTC / USD\"`; `\"UNKNOWN\"` when the source could not be decoded."},"intendedPair":{"type":"string","nullable":true,"description":"What the feed *should* report: `\"<asset> / <numeraire>\"`."},"correctOracle":{"type":"boolean","nullable":true,"description":"Does the feed price the intended **asset** (numerator match)? `null` = unverifiable."},"denominatorMatch":{"type":"boolean","nullable":true,"description":"Is the feed denominated in the right **numeraire** (e.g. the loan token / market unit)? `null` = unknown."},"fixedRate":{"type":"boolean","description":"Hardcoded/constant price feed."},"score":{"type":"integer","description":"Feed risk score 0–100. See `OracleInfo` for the model."},"band":{"type":"string","enum":["LOW","MEDIUM","HIGH","CRITICAL"],"description":"Risk band for `score`."},"flags":{"type":"array","items":{"type":"string"},"description":"Risk flags: `wrong-asset`, `correlated-proxy`, `cross-numeraire`, `undecoded-source`, `fixed-rate`, `unrecognized-provider`."}}},"OracleInfo":{"type":"object","nullable":true,"description":"Oracle feed-correctness classification for the market's price oracle(s). `null` when the market has no oracle classification.\n\nThis is **feed correctness** — does the oracle price the right asset in the right unit — and is distinct from the price-*staleness* signal carried in `risk.breakdown[oracle]` (a 1–5 score). A market can have several feeds (Compound comets price each collateral asset; Fluid prices each vault side), so `feeds` is an array and `worstScore`/`worstBand` summarize the riskiest one.\n\n**Scoring (per feed, additive):** `score = provider base + flag penalties`, clamped 0–100.\n\nProvider base (oracle mechanism; first match wins):\n\n| Provider | Base |\n|---|---|\n| `chainlink`, `price-cap` | 10 |\n| `redstone`/`pyth`/`chronicle`/… and *unrecognized* | 18 |\n| `composite` / cross-feed | 22 |\n| `exchange-rate` / `pendle-pt` / LST rate adapters | 28 |\n| `twap`/`uniswap`/DEX | 30 |\n| `fixed-rate` / `constant` | 55 |\n\nFlag penalties (added on top): `wrong-asset` +45 · `correlated-proxy` +18 · `cross-numeraire` +18 · `undecoded-source` +8.\n\nBands: **LOW** < 25 · **MEDIUM** 25–49 · **HIGH** 50–74 · **CRITICAL** ≥ 75.","required":["feeds","worstScore","worstBand"],"properties":{"feeds":{"type":"array","items":{"$ref":"#/components/schemas/OracleFeed"},"description":"Per-feed classifications, ordered worst-first."},"worstScore":{"type":"integer","description":"Highest (worst) `score` across `feeds`."},"worstBand":{"type":"string","enum":["LOW","MEDIUM","HIGH","CRITICAL"],"description":"Band of the worst feed."}}},"MarketFlags":{"type":"object","description":"Boolean flags describing the operational status of a lending market. Values may be null if unavailable from the protocol.","properties":{"isActive":{"type":"boolean","nullable":true,"description":"Whether the market is active"},"isFrozen":{"type":"boolean","nullable":true,"description":"Whether the market is frozen (no new deposits/borrows)"},"hasStable":{"type":"boolean","nullable":true,"description":"Whether stable-rate borrowing is available"},"borrowingEnabled":{"type":"boolean","nullable":true,"description":"Whether borrowing is enabled"},"depositsEnabled":{"type":"boolean","nullable":true,"description":"Whether deposits are enabled"},"collateralActive":{"type":"boolean","nullable":true,"description":"Whether the asset can be used as collateral"},"variableBorrowDisabled":{"type":"boolean","nullable":true,"description":"Whether variable-rate borrowing is unavailable through 1delta for this market. `true` for Lista DAO fixed-term (brokered) markets, where borrows must go through the broker and pick a fixed term from `terms[]`. Together with a non-empty `terms[]` this is the canonical \"brokered market\" signal — such markets report `variableBorrowRate = 0` but cannot be borrowed variably."}}},"MarketCaps":{"type":"object","description":"Supply, borrow, and debt ceiling caps for a lending market.","properties":{"borrowCap":{"type":"number","nullable":true,"description":"Maximum borrowable amount (token units)"},"supplyCap":{"type":"number","nullable":true,"description":"Maximum depositable amount (token units)"},"debtCeiling":{"type":"string","nullable":true,"description":"Isolation-mode debt ceiling"}}},"MarketConfigEntry":{"type":"object","description":"Risk parameters for a specific e-mode or collateral category.","properties":{"category":{"type":"string","description":"E-mode category identifier","example":"1"},"label":{"type":"string","description":"Human-readable label for this config category","example":"ETH correlated"},"borrowCollateralFactor":{"type":"number","nullable":true,"description":"LTV for borrowing (0-1)","example":0.8},"collateralFactor":{"type":"number","nullable":true,"description":"Liquidation threshold (0-1)","example":0.85},"borrowFactor":{"type":"number","nullable":true,"description":"Borrow factor (typically 1)","example":1},"liquidationPenalty":{"type":"number","nullable":true,"description":"Liquidation penalty for this mode, as a fraction of repaid debt the liquidator receives on top of par (e.g. 0.05 = 5%). Mode-specific where supported (Aave e-modes, Dolomite categories, Euler vaults).","example":0.05},"closeFactor":{"type":"number","nullable":true,"description":"Max fraction of debt repayable per liquidation (0-1). Mirrors the pool-level closeFactor; not e-mode-specific in any supported protocol, so the same value across every mode of a market.","example":0.5},"targetHealthFactor":{"type":"number","nullable":true,"description":"Liquidation target health factor (e.g. 1.05). Set only by protocols that liquidate to a target HF instead of a fixed close factor (Aave V4, spoke-level); omitted otherwise.","example":1.05},"collateralDisabled":{"type":"boolean"},"debtDisabled":{"type":"boolean"}}},"UserRewardEntry":{"type":"object","description":"A claimable reward token entry for a user position.","properties":{"asset":{"type":"string","description":"Reward token contract address","example":"0xc00e94Cb662C3520282E6f5717214004A7f26888"},"totalRewards":{"type":"number","description":"Total accumulated rewards (token units)","example":12.5},"claimableRewards":{"type":"number","description":"Immediately claimable rewards (token units)","example":12.5}}},"RewardAprBreakdown":{"type":"object","description":"APR breakdown for a single reward token.","properties":{"apr":{"type":"number","description":"Net reward APR (normalized to NAV)","example":0.045},"borrowApr":{"type":"number","description":"Reward APR on borrows","example":0.012},"depositApr":{"type":"number","description":"Reward APR on deposits","example":0.078}}},"RewardEntry":{"type":"object","description":"One reward program on a lending market, one entry per (reward token, source). A borrow-side APR REDUCES the cost of borrowing — dTRINITY dLEND is the extreme case, where the rebate exceeds the interest and the net borrow rate is negative. Rewards are NEVER folded into health-factor or liquidation math: those use the gross rate.","properties":{"asset":{"type":"string","description":"Reward token address, lowercased. For a points program (`kind: \"points\"`) there is no token and this is a synthetic `points:<sourceId>` key."},"symbol":{"type":"string","nullable":true,"description":"Reward token symbol. Denormalized deliberately — reward tokens are routinely absent from the asset table (aMonUSDe, WMON, aHorRwaRLUSD are live examples), so a join would leave them unnamed."},"decimals":{"type":"integer","nullable":true},"logoURI":{"type":"string","nullable":true},"depositRate":{"type":"number","description":"Reward APR on deposits"},"variableBorrowRate":{"type":"number","description":"Reward APR on variable borrows"},"stableBorrowRate":{"type":"number","description":"Reward APR on stable borrows"},"kind":{"type":"string","nullable":true,"description":"`token` | `points`. Points are NOT priceable and must be shown separately from any headline APR."},"claim":{"type":"string","nullable":true,"description":"How the reward is realized, i.e. whether the APR is bankable. `accrual` — claimable from the protocol on-chain; `merkl` — off-chain merkle distribution; `manual`."},"source":{"type":"string","description":"LEGACY mechanism tag (`merkle`, `onchain-incentives`, `native`) — it cannot distinguish two programs on the same platform. Prefer `sourceId`."},"sourceId":{"type":"string","nullable":true,"description":"Stable program identifier, safe to key on: `merkl:aave`, `merkl:euler`, `merkl:morpho`, `dtrinity:rebate`."},"sourceLabel":{"type":"string","nullable":true,"description":"Display string for the program, e.g. `Merkl · Aave`."},"link":{"type":"string","nullable":true,"description":"Deep link to THIS program — the exact Merkl opportunity page, not a protocol homepage."},"endsAt":{"type":"number","nullable":true,"description":"Unix seconds the program stops paying. An APR with two weeks left is not the same product as a standing rate, so render it."},"startsAt":{"type":"number","nullable":true},"dailyRewardsUsd":{"type":"number","nullable":true,"description":"Program-wide payout rate in USD/day, as the source reports it."},"refs":{"type":"object","nullable":true,"additionalProperties":true,"description":"Platform identifiers verbatim — Merkl campaign ids and opportunity type, or the incentives-controller address for an on-chain program. For support and deduplication, not display."}}},"FixedTermInfo":{"type":"object","description":"Canonical cross-protocol fixed-term market descriptor, served per lender key on fixed-rate / fixed-maturity markets (Lista brokered, Morpho Midnight, Term Finance, Exactly, Teller, TermMax). Absent on variable-rate lenders. The rate-card menu itself stays on `markets[].terms[]`; this carries the maturity, fee, early-repay and origination-window facts. Per-protocol repay/penalty economics are documented in FIXED_TERM_REPAY_TERMS.md.","properties":{"model":{"type":"string","enum":["lista","midnight","term","exactly","teller","termmax"],"description":"Underlying fixed-term protocol shape.","example":"term"},"maturity":{"type":"integer","nullable":true,"description":"Single fixed calendar maturity, unix seconds. Absent for rolling-duration menus (Lista) and multi-maturity markets (Exactly — the menu lives on `terms[]`)."},"fees":{"type":"object","additionalProperties":true,"description":"Market-level fees: `continuousFeeApr` (%/yr lender-side haircut, Midnight), `settlementFee` (fraction at the current TTM, Midnight), `latePenaltyApr` (%/yr on overdue debt, Exactly), `originationFeePercent` (upfront % of principal, Teller). Empty for lenders without them."},"earlyRepay":{"type":"object","additionalProperties":true,"description":"Early-repayment policy: `{ kind: \"none\" | \"penalty\" | \"discount\" }`. `none` = exit any time at market price; `penalty` = per-loan penalty (amount is position-level); `discount` = repaying early costs LESS than face value (Exactly)."},"provider":{"type":"object","additionalProperties":true,"description":"Who fronts the term: `{ kind: \"broker\" | \"orderbook\" | \"auction\" | \"pool\", address? }`. `auction` markets carry the `auction` window below and can only be borrowed inside a round."},"auction":{"$ref":"#/components/schemas/FixedTermAuction"}}},"FixedTermAuction":{"type":"object","description":"Origination window for a fixed-term market whose terms are only obtainable during a bounded round rather than continuously — Term Finance (`fixedTerm.provider.kind = \"auction\"`). Served on `fixedTerm.auction`; ABSENT on lenders whose terms are continuously available, and absent is NOT the same as `closed`.\n\nThis is the difference between \"the rate card is empty right now\" and \"this market cannot be borrowed at all\": between rounds a Term repo still has a maturity, collateral params and a last-cleared rate, but nothing can be borrowed. Most repos are between rounds at any given time.","properties":{"status":{"type":"string","enum":["upcoming","open","revealing","closed"],"description":"`upcoming` — a round is listed but not yet accepting submissions. `open` — accepting sealed bids/offers. `revealing` — bidding shut, sealed prices revealing and the round clearing. `closed` — no round is listed. Snapshot at fetch time; re-derive from the timestamps against the current clock when reading a cached response.","example":"open"},"canBorrow":{"type":"boolean","description":"Can a NEW borrow be opened right now? **Gate the borrow CTA on this**, not on `status` and not on the presence of a rate — it stays correct if more statuses are added. True only inside an open round: Term borrow origination is a sealed bid, so there is no other entry point.","example":true},"canLend":{"type":"boolean","description":"Can a NEW lend position be opened right now? Deliberately independent of `canBorrow` — the primary auction is only one of two lend surfaces, and buying repo tokens on the secondary market works between rounds. A closed round therefore leaves the market **lend-only**, not inert; greying out the whole market would be wrong.","example":true},"secondsUntilClose":{"type":"integer","nullable":true,"description":"Seconds until submissions close (`revealTime − now`). Absent unless a round is open. A snapshot — for a live countdown derive from `revealTime`, since responses are cached.","example":263000},"implications":{"type":"array","items":{"type":"string"},"description":"Ready-to-display consequences of this market's origination model, most important first (same convention as `params.market.teller.implications`). Auction mechanics are unusual enough that showing only a rate misleads."},"id":{"type":"string","nullable":true,"description":"Auction round id. Absent when `status: \"closed\"`."},"startTime":{"type":"integer","nullable":true,"description":"Submissions open, unix seconds. Absent when closed."},"revealTime":{"type":"integer","nullable":true,"description":"Submissions CLOSE and sealed prices begin revealing, unix seconds — **the deadline to act**. Absent when closed."},"endTime":{"type":"integer","nullable":true,"description":"Round clears, unix seconds. Equal to `revealTime` on current deployments. Absent when closed."},"minBorrowAmount":{"type":"string","nullable":true,"description":"Minimum bid (borrow) size for this round, loan-token BASE units. A real floor (e.g. `\"1000000000\"` = 1000 USDC) — a smaller amount cannot be submitted at all, so validate before building the action rather than surfacing a failed transaction.","example":"1000000000"},"minLendAmount":{"type":"string","nullable":true,"description":"Minimum offer (lend) size for this round, loan-token BASE units.","example":"1000000000"}}},"MarketCapability":{"type":"object","description":"One action this market row supports, declared by the server. **Render the CTA from this, not from lender knowledge** — the same contract the earn rows use (`/v1/data/earn` `capabilities[]`).\n\nThe array is COMPLETE over the actions it models (today: `refinance`, `set-mode`): for those, absent means NOT OFFERED for this row and a client must not offer it. Actions outside that set say nothing about the row — they are not modelled here yet. Nothing about the underlying mechanism leaks into this shape: `refinance` on a Lista brokered market is a flash-loan composer bundle and on Exactly it is the protocol's own DebtManager, and a caller can tell neither apart, which is the point.","properties":{"action":{"type":"string","enum":["refinance","set-mode"],"description":"`refinance` — move this market's debt into a different term without changing the assets or the size (called \"roll-over\" when the source is itself a fixed loan); builds at `/v1/actions/loop/refinance`.\n\n`set-mode` — switch the ACCOUNT's active risk mode (Aave e-mode); builds at `/v1/actions/lending/mode`. Absent on lenders with no switchable mode — including LlamaLend, whose mode slot carries a BAND COUNT fixed at open, and every Morpho/Lista-shaped market, which has no mode at all."},"parameter":{"type":"string","enum":["term","mode"],"description":"Which parameter the action changes. `rate` (the Liquity family's interest-rate adjust) is the same verb on a third knob and appears here when built."},"scope":{"type":"string","enum":["position","account"],"description":"What the change applies to. `position` — acts on ONE loan, which the caller must identify. `account` — one value for the whole (lender, chain, account); it is declared on every row of that lender because it is true of all of them, so a consumer showing one account-level control per lender should read it off any row rather than per market."},"endpoint":{"type":"string","description":"The endpoint that builds this action.","example":"/v1/actions/loop/refinance"},"requires":{"type":"array","items":{"type":"string"},"description":"Params required BEYOND the universal set (`marketUid`, `operator`, `amount`). For `refinance`: `termId` (the TARGET term) — `fromLoanId` is optional by design, omitted means the float/dynamic position. For `set-mode`: `lender`, `chainId` and `mode`, since it is account-scoped and takes no market.","example":["termId"]},"domainField":{"type":"string","enum":["terms","config"],"description":"Which field on this row holds the allowed values for `requires` — never a second copy of it. `terms` is the published rate card; `config` is the risk-config map, whose KEYS are the mode ids and whose `label` is each mode's name."}}},"MarketTerm":{"type":"object","description":"A fixed-term entry in a rate card, in `LendingMarket.terms[]`. Emitted by every fixed-term lender (Lista broker, Midnight, Term Finance, Exactly, Teller, TermMax). Borrowers pick one and pass its `termId` to the borrow action. NOTE `termId` semantics differ per lender — see the field description.","properties":{"termId":{"type":"integer","description":"Term identifier — MEANING IS LENDER-SPECIFIC. Exactly and TermMax: the pool/market unix MATURITY timestamp. Lista: the broker-defined product id. Teller: the duration in seconds (rolling term). Midnight and Term Finance: `0`, a placeholder (single maturity per market, so the id carries no information). Pass to `/v1/actions/lending/borrow?termId=…`. Numeric on-chain; some upstream feeds serialize it as a string — coerce with `Number()` when comparing.","example":2},"depositApr":{"type":"number","nullable":true,"description":"Annualised fixed LEND rate at this maturity, in percent (Exactly only — its fixed pools quote both sides)."},"available":{"type":"number","nullable":true,"description":"Borrowable liquidity at this maturity in loan-token human units (Exactly only)."},"durationDays":{"type":"number","description":"How long the position is locked at the fixed rate, in days (e.g. 7, 14, 30).","example":7},"durationSecs":{"type":"number","nullable":true,"description":"Term duration in seconds (raw on-chain value).","example":604800},"apr":{"type":"number","description":"Annualised borrow APR for this term, in **percent** (e.g. `3.85` = 3.85%). Same unit as `variableBorrowRate` / `stableBorrowRate`. For order-book markets (Midnight) this is the 0-notional top-of-book (cheapest) rate — see `aprAtAmount` for the size-weighted rate.","example":3.85},"aprAtAmount":{"type":"number","nullable":true,"description":"Size-weighted (VWAP) borrow APR % at the supplied debt notional, for order-book fixed-term markets (Morpho Midnight): the borrow book filled cheapest-first, `(Σ filledᵢ·aprᵢ)/amount`. Present only when an amount is supplied AND the term carries an order-book `ladder`; broker rate cards (Lista, single flat rate) omit it and `apr` already applies at any size."},"fillable":{"type":"number","nullable":true,"description":"Total loan-token depth in this term's order book — the maximum borrow openable at this maturity. Present only for order-book terms with an amount supplied."},"capped":{"type":"boolean","nullable":true,"description":"True when the supplied debt notional exceeds `fillable` — the book can't fully fund the borrow at this maturity (`aprAtAmount` is then the drain-the-book VWAP)."},"ladder":{"type":"array","nullable":true,"items":{"type":"object","properties":{"apr":{"type":"number","description":"Annualised borrow rate at this tier (%)."},"units":{"type":"string","description":"Credit/debt units at this tier (raw)."},"assets":{"type":"number","description":"Loan-token size available at this tier."}}},"description":"Order-book borrow ladder (best-borrow first) for order-book fixed-term markets (Midnight). Only serialized when `depth=true` (bulky). `aprAtAmount` is the pre-computed size-weighted rate; use this to re-derive it at any amount."}}},"LoanTerm":{"type":"object","description":"Per-loan fixed-term detail attached to a user position (`LendingPosition.term`), one per open loan. Emitted by Lista broker, Exactly, TermMax and Teller — several fields are lender-specific, and the repay economics behind them differ sharply per protocol (see FIXED_TERM_REPAY_TERMS.md). Lista only: the flexible/dynamic position uses `isDynamic = true` and the `type(uint128).max` `loanId` sentinel.","properties":{"loanId":{"type":"string","description":"The WRITE TARGET for this loan — meaning is lender-specific: Lista = the broker's posId (the dynamic position uses `340282366920938463463374607431768211455`, `type(uint128).max`); Exactly = the maturity as a string (but repay requests send `termId`, never `loanId`); TermMax = the GT NFT id; Teller = the bidId. Pass to `/v1/actions/lending/repay?loanId=…` for the lenders that key on it.","example":"450"},"termId":{"type":"integer","nullable":true,"description":"The rate-card term this loan was opened against (matches a `MarketTerm.termId`). Omitted for the dynamic/flex position. Exactly: equals the maturity, and IS the field a repay request must send.","example":2},"isDynamic":{"type":"boolean","nullable":true,"description":"True for the flexible/variable position (e.g. a matured fixed loan auto-refinanced into the dynamic position). Omitted/false for fixed-term loans.","example":false},"debt":{"type":"string","description":"Outstanding debt for this loan in token units. Lista/Teller (accruing debt): principal + accrued interest. Exactly/TermMax (STATIC face value): the EXIT-NOW cost — for Exactly that is discounted before maturity and penalty-inflated when overdue, so compare with `faceValue` rather than assuming it is the settle-at-maturity amount.","example":"14.5003"},"apr":{"type":"number","nullable":true,"description":"Fixed APR locked for this loan, in **percent**. Omitted for the dynamic position (it tracks the variable rate).","example":3.85},"maturity":{"type":"number","nullable":true,"description":"Unix timestamp (seconds) when the fixed term ends. After maturity interest is frozen and the loan may be refinanced into the dynamic position. Omitted for the dynamic position.","example":1781789025},"termDays":{"type":"number","nullable":true,"description":"Term length in days.","example":7},"accruedInterest":{"type":"string","nullable":true,"description":"Interest accrued so far on this loan, in token units.","example":"0.000358"},"earlyRepayPenalty":{"type":"string","nullable":true,"description":"Cost (token units) to close this loan **before maturity**, on top of principal + accrued interest (≈ half the remaining-term interest). `0` once the loan has matured. To fully close fund `debt + earlyRepayPenalty`; the broker refunds any excess.","example":"0.001211"},"isMatured":{"type":"boolean","nullable":true,"description":"True once the fixed term has ended. What follows differs per lender: Lista freezes interest and a keeper auto-converts the loan into the dynamic position (so it may reappear as the `isDynamic` row); Exactly starts accruing the late penalty below; TermMax opens a 2-hour liquidation window and then settles by physical delivery; Teller DEFAULTS after the market grace period and a liquidator seizes the entire collateral.","example":false},"faceValue":{"type":"string","nullable":true,"description":"Amount owed AT maturity (principal + fee) for static-face-value lenders (Exactly). No accrual index — it grows only via a late penalty where the protocol has one. Compare against `debt` (the exit-now cost) to show the user what waiting or repaying early is worth.","example":"100.0"},"earlyRepayDiscount":{"type":"string","nullable":true,"description":"Exactly only: the REBATE for repaying before maturity (`faceValue − debt`). Exactly never charges an early-repay fee — but this is `0` when the fixed pool has no unassigned earnings left, so an early repay is not a guaranteed saving. Opposite in sign to Lista `earlyRepayPenalty`.","example":"1.0"},"latePenalty":{"type":"string","nullable":true,"description":"Exactly only: penalty accrued so far past maturity (`debt − faceValue`), token units.","example":"2.25"},"latePenaltyPerDay":{"type":"string","nullable":true,"description":"Exactly only: further penalty per additional day overdue, token units. LINEAR on face value, not compounding.","example":"0.225"},"latePenaltyApr":{"type":"number","nullable":true,"description":"Exactly only: the annualized late-penalty rate in percent (~164 %/yr ≙ ~0.45 %/day at time of writing). A mutable market parameter, snapshotted per fetch — do not hardcode it in UI copy.","example":164.24},"secondsLate":{"type":"integer","nullable":true,"description":"Seconds past maturity; `0` until overdue.","example":0}}},"LendingMarket":{"type":"object","description":"Enriched lending market data as returned by /lending and /pools endpoints. Numeric fields are parsed to number | null.","properties":{"lenderKey":{"type":"string","description":"Protocol identifier","example":"AAVE_V3"},"poolId":{"type":"string","description":"Pool/vault address or protocol-specific ID"},"depositRate":{"type":"number","nullable":true,"description":"Deposit APR (percent)"},"variableBorrowRate":{"type":"number","nullable":true,"description":"Variable borrow APR (percent)"},"stableBorrowRate":{"type":"number","nullable":true,"description":"Stable borrow APR (percent)"},"intrinsicYield":{"type":"number","nullable":true,"description":"Intrinsic yield APR from underlying asset (e.g. stETH staking)"},"totalDeposits":{"type":"number","nullable":true,"description":"Total deposits in token units"},"totalDebtStable":{"type":"number","nullable":true,"description":"Total stable debt in token units"},"totalDebt":{"type":"number","nullable":true,"description":"Total variable debt in token units"},"totalLiquidity":{"type":"number","nullable":true,"description":"Available liquidity (totalDeposits - totalDebt) in token units"},"totalDepositsUsd":{"type":"number","nullable":true,"description":"Total deposits in USD"},"totalDebtStableUsd":{"type":"number","nullable":true,"description":"Total stable debt in USD"},"totalDebtUsd":{"type":"number","nullable":true,"description":"Total variable debt in USD"},"totalLiquidityUsd":{"type":"number","nullable":true,"description":"Available liquidity in USD"},"utilization":{"type":"number","nullable":true,"description":"Utilization ratio (totalDebt / totalDeposits)"},"decimals":{"type":"integer","nullable":true},"underlyingInfo":{"$ref":"#/components/schemas/MarketUnderlyingInfo"},"oracleInfo":{"$ref":"#/components/schemas/OracleInfo"},"caps":{"$ref":"#/components/schemas/MarketCaps"},"flags":{"$ref":"#/components/schemas/MarketFlags"},"rewards":{"type":"array","items":{"$ref":"#/components/schemas/RewardEntry"},"description":"Active reward programs. Defaults to [] when none."},"config":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/MarketConfigEntry"},"description":"Risk config keyed by mode/category ID (e.g. \"0\" for default, \"1\" for e-mode)"},"terms":{"type":"array","nullable":true,"items":{"$ref":"#/components/schemas/MarketTerm"},"description":"Fixed-term rate card. Non-empty ⇒ this market offers fixed terms and a borrower picks a `termId`; `null` ⇒ a regular variable-rate market. Served by every fixed-term lender, not only Lista — on Exactly each entry is a maturity. On a Lista brokered market it is also the brokered-market signal (with `flags.variableBorrowDisabled`), and it sits only on the LOAN-token row, never the collateral one.\n\nThis is the value DOMAIN a `capabilities[]` entry points at via `domainField`."},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/MarketCapability"},"description":"Actions this row supports, declared rather than inferred. Complete over the actions it models — see `MarketCapability`. Defaults to `[]`."},"broker":{"type":"string","nullable":true,"description":"Lista DAO `LendingBroker` contract address — the mandatory gateway for the **debt side** (borrow/repay) of a brokered market. Present (non-zero) only for brokered markets. The borrow/repay calldata routes through this contract (the SDK and worker resolve it automatically).","example":"0x1fa26015286d1270343d7526c60bd57ab6be8b54"},"collateralProvider":{"type":"string","nullable":true,"description":"Lista DAO collateral-provider contract for this market. When set (non-zero), Moolah gates `supplyCollateral`/`withdrawCollateral` behind it, so collateral deposits/withdrawals must route through this provider rather than calling Moolah directly (the SDK/worker handle this). Set for markets whose collateral is a Lista-managed token (e.g. slisBNB); `null` for plain ERC-20 collateral.","example":"0x33f7a980a246f9b8fea2254e3065576e127d4d5f"},"loanProvider":{"type":"string","nullable":true,"description":"Lista DAO loan-token provider contract for this market (e.g. the native-WBNB wrapper provider). When set, loan-token operations are gated behind it; for brokered markets the broker handles the debt side. Informational — consumers do not pass it; the SDK/worker resolve routing automatically.","example":"0x367384c54756a25340c63057d87ea22d47fd5701"},"closeFactor":{"type":"number","nullable":true,"description":"Max fraction of a borrower's debt repayable in a single liquidation (0-1). Aave ~0.5 (rises to 1 below the close-factor health threshold), Compound V2 closeFactorMantissa; 1 (full liquidation) for isolated / credit-account protocols (Compound V3, Morpho, Euler, Fluid, Gearbox, Dolomite, Silo).","example":0.5},"targetHealthFactor":{"type":"number","nullable":true,"description":"Liquidation target health factor (e.g. 1.05). Set only by protocols that liquidate to a target HF rather than a fixed close factor (Aave V4, spoke-level); omitted otherwise.","example":1.05},"lenderInfo":{"$ref":"#/components/schemas/LenderInfo"},"termSheet":{"oneOf":[{"$ref":"#/components/schemas/TermSheetDigest"},{"$ref":"#/components/schemas/TermSheet"}],"nullable":true,"description":"Structured description of this market’s lend and borrow offer. Shape depends on the `terms` query param: `digest` (default) or `full`. Absent when `terms=none`."}},"additionalProperties":true},"TermInfo":{"type":"object","required":["headline","tags"],"properties":{"headline":{"type":"string","description":"Ready-to-render one-liner, templated from this market’s live numbers. ALWAYS populated — it is the graceful-degradation path when a client meets an enum member it predates.","example":"Fixed 4.12% until 3 Sep 2026 · repay any time at face value"},"description":{"type":"string"},"implications":{"type":"array","items":{"type":"string"},"description":"Consequences a rate alone hides, ordered MOST SEVERE FIRST. Present only on `terms=full`."},"tags":{"type":"array","items":{"type":"string"},"description":"Machine tags derived from the structured fields (never hand-written), e.g. `time-liquidation`, `full-collateral-seizure`, `redeemable`, `exit-cooldown`. Present in BOTH the digest and the full sheet, which is why severity can be computed from either."}}},"TermFee":{"type":"object","description":"One charge. A NEGATIVE `value` is a REBATE (Exactly rebates early repayment) — sign is load-bearing, never take an absolute value.","required":["id","label","when","unit","basis","value"],"properties":{"id":{"type":"string","description":"Stable slug: `origination`, `late-penalty`, `early-repay-discount`, `reserve-contribution`, `instant-exit`, `performance`, `reserve-factor`, …"},"label":{"type":"string","description":"Self-describing, so an `id` a client does not recognise still renders correctly."},"when":{"type":"string","description":"`entry` | `ongoing` | `exit` | `late` | `liquidation` | `performance`"},"unit":{"type":"string","description":"`apr-percent` | `percent` | `bps` | `absolute`"},"basis":{"type":"string","description":"`principal` | `face-value` | `yield` | `collateral` | `shares` | `debt-repaid`"},"value":{"type":"number"},"payee":{"type":"string","nullable":true},"mutable":{"type":"boolean","nullable":true,"description":"Governance-mutable — a snapshot."},"indicative":{"type":"boolean","nullable":true,"description":"Only resolvable at action time (Exactly’s discount, TermMax’s curve price)."},"description":{"type":"string","nullable":true}}},"TermRate":{"type":"object","description":"All rates are NOMINAL APR in PERCENT (`3.85` = 3.85 %/yr) — never a fraction, never an APY.","required":["kind","apr","components","aprTotal","basis","isLocked"],"properties":{"kind":{"type":"string","description":"`variable-curve` | `variable-managed` | `user-set` | `fixed-term` | `zero-interest` | `prepaid` | `nav-accrual` | `none`. A `zero-interest` market is NOT a free borrow — the cost is a one-off fee."},"apr":{"type":"number","description":"Base rate only."},"components":{"type":"object","properties":{"base":{"type":"number"},"rewards":{"type":"number","nullable":true,"description":"PRICEABLE rewards only."},"intrinsic":{"type":"number","nullable":true}}},"aprTotal":{"type":"number","description":"base + priceable rewards + intrinsic. Deliberately EXCLUDES points programs, which have no priceable value — see `rewards[].indicative`."},"basis":{"type":"string","enum":["apr-nominal"]},"compounding":{"type":"string"},"source":{"type":"string"},"isLocked":{"type":"boolean"},"minApr":{"type":"number","nullable":true},"maxApr":{"type":"number","nullable":true},"userSet":{"type":"object","nullable":true,"description":"Present when the BORROWER sets the rate (Liquity family). A borrow UI must render an input: the rate decides your place in the redemption queue, so the cheapest is also the first redeemed. Omit the rate on the action and the protocol applies `default` (the branch average).","properties":{"required":{"type":"boolean"},"min":{"type":"number","nullable":true},"max":{"type":"number","nullable":true},"default":{"type":"number","nullable":true},"adjustable":{"type":"boolean"},"adjustmentCostNote":{"type":"string","nullable":true},"adjustmentCooldownSecs":{"type":"number","nullable":true}}},"rewards":{"type":"array","nullable":true,"items":{"type":"object","properties":{"asset":{"type":"object","nullable":true,"additionalProperties":true},"kind":{"type":"string","description":"`token` | `points` | `unknown`"},"apr":{"type":"number"},"side":{"type":"string","enum":["supply","borrow"]},"claim":{"type":"string","description":"`accrual` | `merkl` | `manual` | `none`"},"endsAt":{"type":"number","nullable":true},"indicative":{"type":"boolean","nullable":true,"description":"Points — not priceable, and excluded from `aprTotal`."}}}},"menu":{"type":"array","nullable":true,"items":{"$ref":"#/components/schemas/MarketTerm"}}}},"TermMaturity":{"type":"object","required":["kind"],"properties":{"kind":{"type":"string","description":"`perpetual` | `fixed-date` | `rolling-duration`"},"maturity":{"type":"number","nullable":true,"description":"Unix seconds."},"maturityIso":{"type":"string","nullable":true},"secondsToMaturity":{"type":"number","nullable":true},"minDurationSecs":{"type":"number","nullable":true},"maxDurationSecs":{"type":"number","nullable":true},"atMaturity":{"type":"string","nullable":true,"description":"What happens if NOBODY acts — the field that most surprises users. `stops-earning` | `penalty-accrues` | `liquidatable` | `default-seizure` | `physical-delivery` | `refinanced` | `auto-roll` | `none`."},"graceSecs":{"type":"number","nullable":true,"description":"Window before `atMaturity` bites. Observed as low as 300 s (Teller)."}}},"TermRedemption":{"type":"object","description":"Collateral taken from a HEALTHY position. On every lender we serve this is a PERMISSIONLESS ARBITRAGE that defends the stablecoin’s peg — not a liquidation, and not a governance decision. Rendering only the effect (\"your collateral can be taken\") misleads.","required":["trigger","order"],"properties":{"trigger":{"type":"string","description":"`permissionless-arbitrage` — any holder of the debt token, any time. (`governance` / `protocol` are reserved and unused.)"},"driver":{"type":"string","nullable":true,"description":"`below-peg` — it only pays them while the token trades under its target."},"order":{"type":"string","description":"`lowest-rate-first` (Liquity family — your rate IS your queue position) | `pro-rata` (Resupply — every borrower is skimmed, nothing to out-run) | `lowest-collateral-ratio`."},"valueImpact":{"type":"string","nullable":true,"description":"`usd-neutral` — the fee stays in the position as extra collateral, so the borrower loses EXPOSURE, not value. Do not render this as \"you lose your collateral\"."},"defence":{"type":"string","nullable":true,"description":"What the borrower can do. ABSENT when `order: pro-rata` — there is nothing."}}},"TermLiquidation":{"type":"object","required":["trigger","closeFactor","seizure"],"properties":{"model":{"type":"string","nullable":true,"description":"HOW it happens, distinct from what triggers it. `repay-seize` | `soft-band` (LlamaLend: gradual, reversible, penalty-free conversion inside the AMM — there is no single liquidation price) | `stability-pool` | `auction` (Frankencoin: no oracle, an owner-DECLARED price policed by a Dutch auction) | `default-seizure` | `delivery` | `repay-to-target-hf` (Aave V4: no close factor at all — the liquidator repays whatever restores `targetHealthFactor`, and `penalty` is the MAXIMUM bonus, reached at `healthFactorForMaxBonus`) | `none`."},"absorber":{"type":"string","nullable":true},"reversible":{"type":"boolean","nullable":true,"description":"`soft-band` only — being \"in liquidation\" is not terminal and unwinds if price recovers."},"trigger":{"type":"string","description":"`price` | `time` | `price-and-time` | `redemption` | `none`. A `time` trigger liquidates a perfectly over-collateralised position; a health factor does NOT protect you."},"ltv":{"type":"number","nullable":true,"description":"FRACTION (0.8 = 80 %)."},"liquidationLtv":{"type":"number","nullable":true,"description":"FRACTION."},"penalty":{"type":"number","nullable":true,"description":"FRACTION of repaid debt paid to the liquidator on top of par. ABSENT means UNKNOWN — never read it as 0."},"penalties":{"type":"array","nullable":true,"description":"Named penalties where one number cannot express the model — Liquity charges differently depending on whether the Stability Pool absorbs the debt or it is redistributed.","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"value":{"type":"number"},"description":{"type":"string","nullable":true}}}},"closeFactor":{"type":"number","description":"Max FRACTION of the debt repayable in a single liquidation. Aave/Compound V2 `0.5`; `1` (full liquidation) for isolated and credit-account protocols. `1` is also the fallback when the market data carries no close factor, so read it together with `fullCloseBelowHealthFactor`."},"targetHealthFactor":{"type":"number","nullable":true},"seizure":{"type":"string","description":"`proportional` | `full-collateral`. The latter (Teller) means a liquidator takes the ENTIRE escrow, not the amount owed — roughly 2x the borrowed value at 50 % LTV."},"redeemable":{"type":"boolean","nullable":true},"redemption":{"$ref":"#/components/schemas/TermRedemption"},"windowSecs":{"type":"number","nullable":true},"gracePeriodSecs":{"type":"number","nullable":true},"permissioned":{"type":"boolean","nullable":true},"badDebt":{"type":"string","nullable":true},"bandLtv":{"type":"object","nullable":true,"additionalProperties":{"type":"number"},"description":"`soft-band` only: collateral factor as a function of the band count chosen at open (0.991 at N=4 vs 0.886 at N=50). `ltv` reports the default N."},"defaultBands":{"type":"number","nullable":true},"fullCloseBelowHealthFactor":{"type":"number","nullable":true},"healthFactorForMaxBonus":{"type":"number","nullable":true,"description":"`repay-to-target-hf` only: the health factor at or below which the liquidator bonus reaches `penalty`. Between there and HF 1 the bonus is smaller."}}},"TermExposure":{"type":"object","description":"What backs a deposit (`supply.backedBy`) or what may be posted (`borrow.acceptedCollateral`).","required":["count","weightBasis"],"properties":{"count":{"type":"integer"},"weightBasis":{"type":"string","description":"`debt` | `allocation` | `unweighted`. **`unweighted` means the ACCEPTED SET, not a measured split** — pooled lenders do not record on-chain which collateral backs which borrow, so `weightPct` is absent and a pie chart would be fabricated."},"worstRiskScore":{"type":"number","nullable":true,"description":"1 (best) … 5 (worst)."},"worstOracleBand":{"type":"string","nullable":true},"topWeightPct":{"type":"number","nullable":true},"items":{"type":"array","nullable":true,"description":"Omitted on `terms=digest`; each entry carries its own `marketUid` for resolution.","items":{"type":"object","properties":{"asset":{"type":"object","additionalProperties":true},"marketUid":{"type":"string","nullable":true},"via":{"type":"string"},"weightPct":{"type":"number","nullable":true},"ltv":{"type":"number","nullable":true},"liquidationLtv":{"type":"number","nullable":true},"oracle":{"$ref":"#/components/schemas/TermOracle"},"quality":{"type":"object","nullable":true,"additionalProperties":true}}}}}},"TermOracle":{"type":"object","nullable":true,"description":"The market’s price input. SINGULAR by construction at `marketUid` granularity — verified across the full classification, 0 markets carry more than one address.","properties":{"kind":{"type":"string","description":"`price-feed` | `nav-attested` | `none`. **`none` is a FACT, not missing data** (Teller liquidates on time and has no feed anywhere in its trigger; Frankencoin uses an owner-declared price)."},"address":{"type":"string","nullable":true,"description":"Lowercased. The contract the protocol actually calls."},"components":{"type":"array","nullable":true,"items":{"type":"string"}},"provider":{"type":"string","nullable":true},"priceDescription":{"type":"string","nullable":true},"intendedPair":{"type":"string","nullable":true},"correctAsset":{"type":"boolean","nullable":true},"correctNumeraire":{"type":"boolean","nullable":true},"score":{"type":"integer","nullable":true},"band":{"type":"string","nullable":true,"enum":["LOW","MEDIUM","HIGH","CRITICAL"]},"flags":{"type":"array","nullable":true,"items":{"type":"string"}},"mutability":{"type":"object","nullable":true,"additionalProperties":true}}},"TermGovernance":{"type":"object","properties":{"mutability":{"type":"string","description":"`immutable` | `governed` | `unknown`"},"controller":{"type":"string","nullable":true},"controllerKind":{"type":"string","nullable":true,"description":"EOA | SAFE | TIMELOCK | GOVERNOR | GOVERNANCE | CUSTOM | UNKNOWN"},"safe":{"type":"object","nullable":true,"additionalProperties":true},"timelockSecs":{"type":"number","nullable":true,"description":"The holder’s NOTICE PERIOD before a queued parameter change lands. **NOT a withdrawal lock** — that is `supply.exit.cooldownSecs`. Never merge or sum the two."},"timelockUnknown":{"type":"boolean","nullable":true,"description":"The controller IS a timelock but its delay could not be read. Distinct from \"no delay\" — reporting the latter would be a false alarm on the safest governance shape."},"tier":{"type":"string","nullable":true},"score":{"type":"number","nullable":true},"powers":{"type":"array","nullable":true,"items":{"type":"string"}},"roles":{"type":"object","nullable":true,"additionalProperties":true},"asOfScreen":{"type":"number","nullable":true,"description":"Governance screens refresh far slower than rates."}}},"TermUtilization":{"type":"object","required":["utilization","basis"],"properties":{"utilization":{"type":"number","description":"0..1 FRACTION."},"basis":{"type":"string","description":"`market` | `hub` | `liquidity-layer` | `pool`. NOT always this row: Fluid / Aave V4 / Gearbox set rates on a LARGER pool, and a rate simulation must shift `irmTotal*`, not the row totals."},"irmTotalDeposits":{"type":"number","nullable":true},"irmTotalDebt":{"type":"number","nullable":true},"targetUtilization":{"type":"number","nullable":true},"kinkUtilization":{"type":"number","nullable":true},"supplyCapUtilization":{"type":"number","nullable":true},"borrowCapUtilization":{"type":"number","nullable":true},"lockupRatio":{"type":"number","nullable":true}}},"TermAvailability":{"type":"object","required":["canOpen","canClose","gating"],"properties":{"canOpen":{"type":"boolean","description":"Gate the CTA on THIS and nothing else — it already folds in caps, freezes, auction windows and gating."},"canClose":{"type":"boolean"},"blockedBy":{"type":"string","nullable":true},"gating":{"type":"string"},"minSize":{"type":"string","nullable":true,"description":"Minimum to OPEN, RAW base units of this side’s asset. On borrow a minimum DEBT (Comet `baseBorrowMin`, Liquity `minDebt`, Maker `dust`, Resupply `minimumBorrowAmount`); on supply a minimum COLLATERAL (Frankencoin). A smaller amount REVERTS — validate before building."},"cap":{"type":"string","nullable":true},"capUtilization":{"type":"number","nullable":true},"requires":{"type":"array","nullable":true,"items":{"type":"string"}},"window":{"type":"object","nullable":true,"additionalProperties":true}}},"TermSupplySide":{"type":"object","required":["role","rate","maturity","exit","fees","counterparty","availability","principal","info"],"properties":{"role":{"type":"string","description":"`yield` | `collateral` | `both`"},"rate":{"$ref":"#/components/schemas/TermRate"},"maturity":{"$ref":"#/components/schemas/TermMaturity"},"exit":{"type":"object","properties":{"mode":{"type":"string"},"settlement":{"type":"string","description":"`sync` | `async`"},"cooldownSecs":{"type":"number","nullable":true,"description":"How long YOUR money is locked."},"liquidity":{"type":"object","nullable":true,"additionalProperties":true},"partialAllowed":{"type":"boolean"},"priceRisk":{"type":"string","description":"`none` | `haircut-formula` | `market-price` | `may-be-impossible`"},"fees":{"type":"array","items":{"$ref":"#/components/schemas/TermFee"}}}},"fees":{"type":"array","items":{"$ref":"#/components/schemas/TermFee"}},"backedBy":{"$ref":"#/components/schemas/TermExposure"},"modes":{"type":"array","nullable":true,"items":{"type":"object","additionalProperties":true}},"counterparty":{"type":"object","additionalProperties":true},"availability":{"$ref":"#/components/schemas/TermAvailability"},"principal":{"type":"object","additionalProperties":true},"info":{"$ref":"#/components/schemas/TermInfo"}}},"TermBorrowSide":{"type":"object","required":["rate","maturity","debtShape","exit","liquidation","fees","counterparty","availability","info"],"properties":{"rate":{"$ref":"#/components/schemas/TermRate"},"maturity":{"$ref":"#/components/schemas/TermMaturity"},"debtShape":{"type":"string","description":"`accruing` | `static-face` | `prepaid`. The biggest departure from variable-rate intuition: most fixed-term debt is a static face value fixed at trade time, so repaying early does not reduce it."},"exit":{"type":"object","properties":{"earlyRepay":{"type":"string","description":"`free` | `discount` | `penalty` | `market-price` | `not-allowed`. Note `discount` is a REBATE (Exactly) — assuming \"early = penalty\" is wrong in both directions."},"atMaturityCost":{"type":"string"},"lateBehaviour":{"type":"string"},"partialAllowed":{"type":"boolean"},"minDebt":{"type":"string","nullable":true,"description":"Dust floor, RAW base units — bounds partial repayment too."},"overRepayReverts":{"type":"boolean","nullable":true,"description":"Midnight: over-repay REVERTS; size exactly."},"fees":{"type":"array","items":{"$ref":"#/components/schemas/TermFee"}}}},"liquidation":{"$ref":"#/components/schemas/TermLiquidation"},"acceptedCollateral":{"$ref":"#/components/schemas/TermExposure"},"modes":{"type":"array","nullable":true,"description":"Non-default risk categories as DELTAS (Aave e-modes, Dolomite categories, Euler configs). `liquidation` above is the fully-resolved DEFAULT.","items":{"type":"object","additionalProperties":true}},"fees":{"type":"array","items":{"$ref":"#/components/schemas/TermFee"}},"counterparty":{"type":"object","additionalProperties":true},"availability":{"$ref":"#/components/schemas/TermAvailability"},"info":{"$ref":"#/components/schemas/TermInfo"}}},"TermSheet":{"type":"object","description":"The full term sheet (`?terms=full`). Absence of a side is MEANINGFUL: no `borrow` means the market cannot be borrowed.","required":["schemaVersion","asOf","profileId"],"properties":{"schemaVersion":{"type":"integer","example":1},"asOf":{"type":"number","description":"Unix seconds — everything here is a snapshot."},"profileId":{"type":"string","example":"exactly.fixed@v1"},"marketUid":{"type":"string"},"lender":{"type":"string"},"chainId":{"type":"string"},"asset":{"type":"object","nullable":true,"additionalProperties":true,"description":"The market’s own underlying — needed to render `minSize` / `cap` / `minDebt`, which are RAW base units."},"supply":{"$ref":"#/components/schemas/TermSupplySide"},"borrow":{"$ref":"#/components/schemas/TermBorrowSide"},"governance":{"$ref":"#/components/schemas/TermGovernance"},"oracle":{"$ref":"#/components/schemas/TermOracle"},"utilization":{"$ref":"#/components/schemas/TermUtilization"},"constraints":{"type":"object","nullable":true,"additionalProperties":true},"coverage":{"type":"object","nullable":true,"description":"Distinguishes \"does not apply here\" (`notApplicable`) from \"not classified yet\" (`pending`). A missing block is NEVER a claim of absence.","additionalProperties":true}}},"TermSheetDigest":{"type":"object","description":"The compact form (`?terms=digest`, the DEFAULT). Structurally different from the full sheet: `headline` and `tags` are HOISTED to each side root and there is no `info` object, no nested blocks and no `items[]`. Read it through a shape-tolerant accessor — `sheet.supply.info.tags` throws here.","properties":{"schemaVersion":{"type":"integer"},"profileId":{"type":"string"},"marketUid":{"type":"string"},"supply":{"type":"object","additionalProperties":true},"borrow":{"type":"object","additionalProperties":true},"oracle":{"type":"object","additionalProperties":true},"governance":{"type":"object","additionalProperties":true},"utilization":{"type":"number","nullable":true}}},"LendingLatestItem":{"type":"object","description":"Aggregated lending data for a single lender on a single chain.","required":["chainId","lenderInfo","totalDepositsUsd","totalDebtUsd","tvlUsd","markets"],"properties":{"chainId":{"type":"string","example":"8453"},"lenderInfo":{"$ref":"#/components/schemas/LenderInfo"},"lastFetched":{"type":"number","nullable":true,"description":"Epoch ms of latest snapshot"},"totalDepositsUsd":{"type":"number","description":"Total deposits across all markets in USD"},"totalDebtUsd":{"type":"number","description":"Total debt across all markets in USD"},"tvlUsd":{"type":"number","description":"Total value locked in USD (deposits - debt)"},"params":{"type":"object","nullable":true,"additionalProperties":true,"description":"Lender-specific parameters. Only present for Morpho/Lista lenders (e.g. `{ market: { … } }`)."},"fixedTerm":{"allOf":[{"$ref":"#/components/schemas/FixedTermInfo"}],"nullable":true,"description":"Fixed-term descriptor for this lender key. Absent/null on variable-rate lenders. For Term Finance read `fixedTerm.auction.canBorrow` before offering a borrow — most repos sit between auction rounds and cannot be borrowed even though they quote a rate."},"markets":{"type":"array","items":{"$ref":"#/components/schemas/LendingMarket"},"description":"Individual lending markets for this lender on this chain"}}},"LendingLatestResponse":{"type":"object","description":"Latest lending market data as a flat list of lender/chain entries. Returns only the lender keys requested via `lenders=…` (max 20 per request).","required":["count","items"],"properties":{"count":{"type":"integer","description":"Number of lender/chain entries"},"items":{"type":"array","items":{"$ref":"#/components/schemas/LendingLatestItem"},"description":"Flat array of lender/chain entries"}}},"LendingLendersItem":{"type":"object","description":"Lightweight enumeration entry for a single (chainId, lenderKey) pair.","required":["chainId","lenderInfo","tvlUsd"],"properties":{"chainId":{"type":"string","example":"1"},"lenderInfo":{"$ref":"#/components/schemas/LenderInfo"},"tvlUsd":{"type":"number","description":"Σ totalDepositsUsd − Σ totalDebtUsd over the lender's markets on this chain","example":1234567890.12},"lastFetched":{"type":"number","nullable":true,"description":"Epoch ms of latest snapshot"}}},"LendingLendersResponse":{"type":"object","description":"Enumeration of available (chainId, lenderKey) pairs sorted by tvlUsd descending.","required":["count","items"],"properties":{"count":{"type":"integer","description":"Number of items returned"},"items":{"type":"array","items":{"$ref":"#/components/schemas/LendingLendersItem"},"description":"Lender entries sorted by tvlUsd descending"}}},"LendingSnapshotResponse":{"type":"object","properties":{"markets":{"type":"integer","description":"Number of distinct markets"},"totalPoints":{"type":"integer","description":"Total data points across all markets"},"series":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/LendingSnapshotPoint"}},"description":"Map of marketUid → array of {dataTs, ...fields}"}}},"LendingSnapshotPoint":{"type":"object","description":"Contains dataTs plus whichever fields were requested.","properties":{"dataTs":{"type":"string","format":"date-time"},"depositRate":{"type":"number"},"variableBorrowRate":{"type":"number"},"stableBorrowRate":{"type":"number"},"totalDeposits":{"type":"number"},"totalDebtStable":{"type":"number"},"totalDebt":{"type":"number"},"totalLiquidity":{"type":"number"},"totalDepositsUsd":{"type":"number"},"totalDebtStableUsd":{"type":"number"},"totalDebtUsd":{"type":"number"},"totalLiquidityUsd":{"type":"number"}}},"Pool":{"type":"object","description":"Paginated pool row (snake_case, from DB). Used by /pools endpoint.","properties":{"chain_id":{"type":"string"},"lender_key":{"type":"string"},"underlying_address":{"type":"string"},"asset_group":{"type":"string"},"deposit_rate":{"type":"number"},"variable_borrow_rate":{"type":"number"},"stable_borrow_rate":{"type":"number"},"intrinsic_yield":{"type":"number"},"utilization":{"type":"number"},"total_deposits":{"type":"number"},"total_debt":{"type":"number"},"total_liquidity":{"type":"number"},"total_deposits_usd":{"type":"number"},"total_debt_usd":{"type":"number"},"total_liquidity_usd":{"type":"number"}}},"PoolsResponse":{"type":"object","properties":{"start":{"type":"integer"},"count":{"type":"integer"},"pools":{"type":"array","items":{"$ref":"#/components/schemas/Pool"}}}},"PoolWithMeta":{"allOf":[{"$ref":"#/components/schemas/LendingMarket"}],"type":"object","description":"Enriched pool returned by /pools/latest. Extends LendingMarket with chain context, computed APR, price data, and exposure info.","properties":{"chainId":{"type":"string","description":"Chain ID"},"lender":{"type":"string","description":"Protocol identifier"},"apr":{"type":"number","description":"Effective APR (depositRate + intrinsicYield)"},"price":{"type":"number","nullable":true,"description":"Current asset price in USD"},"histPrice":{"type":"number","nullable":true,"description":"Asset price 24h ago in USD"},"exposure":{"type":"array","items":{"type":"object","properties":{"chainId":{"type":"string"},"lender":{"type":"string"},"poolId":{"type":"string"}},"additionalProperties":true},"description":"Cross-protocol exposure for the same underlying asset"}},"additionalProperties":true},"PoolsLatestResponse":{"type":"object","description":"Enriched pool list returned by /pools/latest.","properties":{"fetchedAt":{"type":"number","description":"Epoch timestamp of last data fetch"},"chainIds":{"type":"array","items":{"type":"string"}},"pools":{"type":"array","items":{"$ref":"#/components/schemas/PoolWithMeta"}}}},"LeveragePair":{"type":"object","description":"Leverage pair with full rate, risk-factor, and liquidity fields for both collateral (long) and debt (short) sides.","properties":{"chainId":{"type":"string"},"lender":{"type":"string"},"marketLongUid":{"type":"string","description":"Market UID of the collateral side"},"marketShortUid":{"type":"string","description":"Market UID of the debt side"},"marketNameLong":{"type":"string","nullable":true,"description":"Display name of the collateral market/vault (e.g. the Euler eVault name). Disambiguates rows that share the collateral/debt token symbols and lender."},"marketNameShort":{"type":"string","nullable":true,"description":"Display name of the debt market/vault. For Euler this is the controller (debt) eVault — the primary way to tell otherwise-identical WETH→USDC rows apart."},"curatorNameLong":{"type":"string","nullable":true,"description":"Curator/brand of the collateral market (Euler: resolved from the vault governor). Null for lenders without a curator, or until the curator registry is seeded. Render as \"curatorName + symbol\", falling back to marketNameLong."},"curatorNameShort":{"type":"string","nullable":true,"description":"Curator/brand of the debt (controller) market. Same semantics as curatorNameLong."},"assetLong":{"type":"string","description":"Collateral asset address"},"assetShort":{"type":"string","description":"Debt asset address"},"assetGroupLong":{"type":"string"},"assetGroupShort":{"type":"string"},"symbolLong":{"type":"string","description":"Collateral token symbol"},"nameLong":{"type":"string","description":"Collateral token name"},"symbolShort":{"type":"string","description":"Debt token symbol"},"nameShort":{"type":"string","description":"Debt token name"},"collateralFactorLong":{"type":"number","description":"Liquidation collateral factor for the long side","example":0.94},"borrowCollateralFactorLong":{"type":"number","description":"Borrow-adjusted collateral factor for the long side","example":0.92},"borrowFactorLong":{"type":"number","description":"Borrow factor for the long side","example":1},"collateralDisabledLong":{"type":"boolean","description":"Whether collateral is disabled for the long asset"},"debtDisabledLong":{"type":"boolean","description":"Whether debt is disabled for the long asset"},"collateralFactorShort":{"type":"number","description":"Liquidation collateral factor for the short side","example":0.94},"borrowCollateralFactorShort":{"type":"number","description":"Borrow-adjusted collateral factor for the short side","example":0.92},"borrowFactorShort":{"type":"number","description":"Borrow factor for the short side","example":1},"collateralDisabledShort":{"type":"boolean","description":"Whether collateral is disabled for the short asset"},"debtDisabledShort":{"type":"boolean","description":"Whether debt is disabled for the short asset"},"eModeConfigId":{"type":"string","description":"E-mode configuration ID"},"eMode":{"type":"string","description":"E-mode category"},"aprBase":{"type":"number","description":"Base APR (deposit - borrow + intrinsic, before rewards)"},"aprTotal":{"type":"number","description":"Total APR (base + rewards)"},"maxLeverage":{"type":"number"},"ltv":{"type":"number","description":"Loan-to-value ratio (0-1)"},"depositRateLong":{"type":"number"},"variableBorrowRateShort":{"type":"number"},"intrinsicYieldLong":{"type":"number"},"intrinsicYieldShort":{"type":"number"},"variableBorrowDisabledShort":{"type":"boolean","nullable":true,"description":"True when the debt (short) market is a Lista DAO brokered market — it cannot be looped at a variable rate, only at one of the fixed terms in `termsShort`. `variableBorrowRateShort` is `0`/undefined for such pairs."},"termsShort":{"type":"array","nullable":true,"items":{"$ref":"#/components/schemas/MarketTerm"},"description":"Fixed-term rate card for the debt (short) side when it is a Lista DAO brokered market. Each entry is one loop option — see the per-term net-APR recipe. `null`/empty for regular variable-rate pairs. For Term Finance an empty card means \"not borrowable right now\" rather than \"no offers\" — read `fixedTerm.auction` for why."},"fixedTerm":{"allOf":[{"$ref":"#/components/schemas/FixedTermInfo"}],"nullable":true,"description":"Fixed-term descriptor for this pair's lender, joined by lender key. Absent on variable-rate lenders. **For Term Finance (`model: \"term\"`), gate the borrow/loop CTA on `fixedTerm.auction.canBorrow`**: origination only happens inside scheduled sealed-bid auction rounds and most repos sit between rounds, so a pair can carry a maturity, an LTV and a rate and still be impossible to borrow. Note also that `aprBase`/`aprTotal` on such a pair are computed against a `variableBorrowRate` of 0 and therefore read as an enormous leveraged yield with a free debt leg — show them as indicative, not obtainable, whenever `canBorrow` is false."},"rewardAprLong":{"type":"number","description":"Total reward APR on the collateral side"},"rewardAprShort":{"type":"number","description":"Total reward APR on the debt side"},"rewardsLong":{"type":"array","nullable":true,"items":{"type":"object","additionalProperties":true},"description":"Reward programs for the collateral side"},"rewardsShort":{"type":"array","nullable":true,"items":{"type":"object","additionalProperties":true},"description":"Reward programs for the debt side"},"totalDepositsLong":{"type":"number","description":"Total deposits in token units (long side)"},"totalDebtLong":{"type":"number","description":"Total debt in token units (long side)"},"totalLiquidityLong":{"type":"number","description":"Total liquidity in token units (long side)"},"totalDepositsShort":{"type":"number","description":"Total deposits in token units (short side)"},"totalDebtShort":{"type":"number","description":"Total debt in token units (short side)"},"totalLiquidityShort":{"type":"number","description":"Total liquidity in token units (short side)"},"totalDepositsUsdLong":{"type":"number"},"totalDebtUsdLong":{"type":"number"},"totalLiquidityUsdLong":{"type":"number"},"totalDepositsUsdShort":{"type":"number"},"totalDebtUsdShort":{"type":"number"},"totalLiquidityUsdShort":{"type":"number"},"borrowLiquidityShort":{"type":"number","description":"Available borrow liquidity (debt side) in token units"},"withdrawLiquidityLong":{"type":"number","description":"Available withdraw liquidity (collateral side) in token units"},"depositableLong":{"type":"number","description":"Remaining deposit capacity (collateral side) in token units"},"utilizationLong":{"type":"number"},"utilizationShort":{"type":"number"},"underlyingInfoLong":{"type":"object","description":"Collateral asset metadata including token info, market prices, and oracle prices","properties":{"asset":{"type":"object","additionalProperties":true,"description":"Token metadata (address, symbol, name, decimals, logoURI, assetGroup)"},"prices":{"type":"object","additionalProperties":true,"description":"Market prices (priceUsd, priceUsd24h, priceChange24h)"},"oraclePrice":{"type":"object","additionalProperties":true,"description":"On-chain oracle prices (oraclePrice, oraclePriceUsd)"}}},"underlyingInfoShort":{"type":"object","description":"Debt asset metadata including token info, market prices, and oracle prices","properties":{"asset":{"type":"object","additionalProperties":true,"description":"Token metadata (address, symbol, name, decimals, logoURI, assetGroup)"},"prices":{"type":"object","additionalProperties":true,"description":"Market prices (priceUsd, priceUsd24h, priceChange24h)"},"oraclePrice":{"type":"object","additionalProperties":true,"description":"On-chain oracle prices (oraclePrice, oraclePriceUsd)"}}}},"additionalProperties":true},"OptimizerPair":{"type":"object","description":"Optimizer pair for collateral/debt searches. May include computed maxDebtAmount or minCollateralAmount.","properties":{"chainId":{"type":"string"},"lender":{"type":"string"},"fixedTerm":{"allOf":[{"$ref":"#/components/schemas/FixedTermInfo"}],"nullable":true,"description":"Fixed-term descriptor for this pair's lender, joined by lender key. Absent on variable-rate lenders. **For Term Finance (`model: \"term\"`), gate the borrow/loop CTA on `fixedTerm.auction.canBorrow`**: origination only happens inside scheduled sealed-bid auction rounds and most repos sit between rounds, so a pair can carry a maturity, an LTV and a rate and still be impossible to borrow. Note also that `aprBase`/`aprTotal` on such a pair are computed against a `variableBorrowRate` of 0 and therefore read as an enormous leveraged yield with a free debt leg — show them as indicative, not obtainable, whenever `canBorrow` is false."},"marketLongUid":{"type":"string","description":"Market UID of the collateral side"},"marketShortUid":{"type":"string","description":"Market UID of the debt side"},"marketNameLong":{"type":"string","nullable":true,"description":"Display name of the collateral market/vault (e.g. the Euler eVault name). Disambiguates rows that share the collateral/debt token symbols and lender."},"marketNameShort":{"type":"string","nullable":true,"description":"Display name of the debt market/vault. For Euler this is the controller (debt) eVault — the primary way to tell otherwise-identical WETH→USDC rows apart."},"curatorNameLong":{"type":"string","nullable":true,"description":"Curator/brand of the collateral market (Euler: resolved from the vault governor). Null for lenders without a curator, or until the curator registry is seeded. Render as \"curatorName + symbol\", falling back to marketNameLong."},"curatorNameShort":{"type":"string","nullable":true,"description":"Curator/brand of the debt (controller) market. Same semantics as curatorNameLong."},"assetLong":{"type":"string"},"assetShort":{"type":"string"},"assetGroupLong":{"type":"string"},"assetGroupShort":{"type":"string"},"symbolLong":{"type":"string","description":"Collateral token symbol"},"nameLong":{"type":"string","description":"Collateral token name"},"symbolShort":{"type":"string","description":"Debt token symbol"},"nameShort":{"type":"string","description":"Debt token name"},"aprBase":{"type":"number","description":"Leverage-weighted net APR % EXCLUDING rewards — the SUSTAINABLE rate (reward incentives are typically transient)."},"aprTotal":{"type":"number","description":"Leverage-weighted net APR % INCLUDING rewards. The reward contribution is aprTotal − aprBase."},"maxLeverage":{"type":"number"},"ltv":{"type":"number"},"depositAprLong":{"type":"number","description":"Effective deposit APR (depositRate + intrinsicYield)"},"borrowAprShort":{"type":"number","description":"Effective borrow APR % (borrowRate + intrinsicYield − rewards, plus any 1y-amortized origination fee — see originationFeeShort). For Liquity-family CDPs the amortized origination fee is the whole borrow cost (variable rate is 0)."},"originationFeeShort":{"type":"number","nullable":true,"description":"One-time origination / mint fee on the debt side, PERCENT (Liquity-family CDPs: River, Felix, Nerite, Ebisu, Soneta, USDAf, Liquity). NOT an APR — it is already folded (1y-amortized) into borrowAprShort / aprTotal, and surfaced raw so consumers can re-amortize over a different holding horizon. Null/absent for markets without one."},"totalDepositsUsdLong":{"type":"number"},"totalDepositsUsdShort":{"type":"number"},"totalDebtUsdLong":{"type":"number"},"totalDebtUsdShort":{"type":"number"},"totalLiquidityUsdLong":{"type":"number"},"totalLiquidityUsdShort":{"type":"number"},"borrowLiquidityShort":{"type":"number"},"utilizationLong":{"type":"number"},"utilizationShort":{"type":"number"},"maxDebtAmount":{"type":"number","description":"Max borrowable amount given collateral (only when collateralAmount/collateralAmountUsd param provided)"},"maxDebtAmountUsd":{"type":"number","description":"USD value of maxDebtAmount"},"minCollateralAmount":{"type":"number","description":"Min collateral needed for debt amount (only when debtAmount/debtAmountUsd param provided)"},"minCollateralAmountUsd":{"type":"number","description":"USD value of minCollateralAmount"},"borrowAprAtAmount":{"type":"number","nullable":true,"description":"Effective borrow APR % at the computed debt notional: the headline borrowAprShort with only its organic (IRM) component re-priced at the post-borrow utilization (intrinsic + rewards are size-invariant). Present when an amount is supplied; null for non-curve lenders and brokered debt markets."},"depositAprAtAmount":{"type":"number","nullable":true,"description":"Effective supply APR % at the collateral notional: the headline depositAprLong with only its organic (IRM) component re-priced at the post-deposit utilization. Present when an amount is supplied; null for non-curve lenders."},"netAprAtAmount":{"type":"number","nullable":true,"description":"Leverage-weighted net position APR % on equity at the supplied amount, using the effective legs (INCLUDING rewards). Same components as aprTotal (directly comparable), but at the position's actual size/leverage. Null when equity is non-positive or a leg is unavailable."},"netAprAtAmountBase":{"type":"number","nullable":true,"description":"Net APR % at the supplied amount EXCLUDING rewards — the sustainable at-size rate (netAprAtAmount with per-leg rewards stripped). The at-size reward contribution is netAprAtAmount − netAprAtAmountBase. Null when netAprAtAmount is."},"borrowDepthShort":{"type":"object","additionalProperties":true,"nullable":true,"description":"Debt-market rate-vs-amount borrow grid. Only when depth=true."},"supplyDepthLong":{"type":"object","additionalProperties":true,"nullable":true,"description":"Collateral-market rate-vs-amount supply grid. Only when depth=true."},"risk":{"type":"object","description":"Per-dimension risk for the pair. No composite headline score — take the worst (highest) breakdown entry if you need one.","properties":{"maxTokenScore":{"type":"integer","nullable":true,"description":"Worse of the two sides' token risk scores."},"breakdown":{"type":"array","description":"One entry per dimension: config (market/e-mode configuration), chain, lender (protocol), tokenLong (collateral asset), tokenShort (debt asset), and curation — the last present ONLY for lenders that have curators (Morpho Blue, Euler). Scores are 1-5, higher = riskier; 0/null means unassessed and is labelled \"unknown\".","items":{"type":"object","properties":{"category":{"type":"string","example":"lender"},"score":{"type":"integer","nullable":true},"label":{"type":"string","enum":["low","medium","high","unknown"]},"curatorIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"curation only: curator slugs of the pair's two markets, unioned. The pair's curation score is the WORSE of its two legs — an Euler position spanning a curated collateral vault and an uncurated controller is only as curated as the controller."}}}}}}}},"LeveragePairsResponse":{"type":"object","properties":{"start":{"type":"integer"},"count":{"type":"integer"},"items":{"type":"array","items":{"$ref":"#/components/schemas/LeveragePair"}}}},"OptimizerPairsResponse":{"type":"object","properties":{"chainIds":{"type":"array","items":{"type":"string"}},"collaterals":{"type":"array","items":{"type":"string"}},"debts":{"type":"array","items":{"type":"string"}},"collateralAmount":{"type":"number"},"collateralAmountUsd":{"type":"number"},"debtAmount":{"type":"number"},"debtAmountUsd":{"type":"number"},"start":{"type":"integer"},"count":{"type":"integer"},"items":{"type":"array","items":{"$ref":"#/components/schemas/OptimizerPair"}}}},"ComparableRateHorizon":{"type":"object","description":"How the effective rate was arrived at — the caveats a UI must show next to a normalized number.","properties":{"basis":{"type":"string","enum":["flat-forward","early-exit","held-to-maturity","rolled"],"description":"`flat-forward`: no maturity, today’s floating rate assumed to hold. `early-exit`: horizon shorter than the term, the venue’s exit rule applied. `held-to-maturity`: horizon matches the term (the clean case). `rolled`: horizon outlasts the term, a roll at today’s rate assumed."},"locked":{"type":"boolean","description":"Is the rate contractually fixed for the WHOLE horizon? False for floating pools and for a fixed term that has to be rolled to cover the horizon."},"priceRisk":{"type":"boolean","description":"Exiting early means unwinding on an order book at the then-current price (Midnight/Term), so the realized cost can land either side of the quote. Not priced in — flagged."},"assumptions":{"type":"array","items":{"type":"string"},"description":"Display-ready caveats, most important first."}}},"ComparableRateDepth":{"type":"object","properties":{"fillable":{"type":"number","nullable":true,"description":"Depth ceiling in token units of the priced leg — borrow liquidity for a pool, total book depth for an order book."},"capped":{"type":"boolean","description":"The requested amount exceeds `fillable`: this venue cannot fund the position at that size, and the row is demoted below every non-capped one."},"liquidityUsd":{"type":"number","nullable":true},"utilization":{"type":"number","nullable":true}}},"ComparableRateAsset":{"type":"object","properties":{"address":{"type":"string"},"assetGroup":{"type":"string"},"symbol":{"type":"string"},"decimals":{"type":"number","nullable":true},"logoUri":{"type":"string","nullable":true},"marketUid":{"type":"string"}}},"ComparableRate":{"type":"object","description":"One comparable venue/term. Three rates are kept deliberately apart: `aprPct` is what the venue advertises (0 notional / top of book), `aprAtAmountPct` is that rate at the requested size, `effectiveAprPct` is the size-priced rate normalized to the requested horizon — and is what `rank` sorts on.","properties":{"rank":{"type":"integer","description":"1-based position in the ranking. `0` on the `reference` item."},"chainId":{"type":"string"},"lender":{"type":"string","description":"Raw lender key — stable, use it for keying and deeplinks. Per-market lenders (Morpho Blue, Silo, Euler) encode a hashed market id here, so it is NOT presentable."},"lenderName":{"type":"string","description":"Display name from the lender registry, falling back to the raw key when none is registered. This is what a UI should render."},"lenderLogoUri":{"type":"string","nullable":true},"marketUid":{"type":"string","description":"The market the rate belongs to: the debt market for a borrow quote, the collateral market for a supply quote."},"marketName":{"type":"string","nullable":true},"curatorName":{"type":"string","nullable":true},"eMode":{"type":"string","nullable":true},"rateType":{"type":"string","enum":["fixed","float"]},"rateModel":{"type":"string","enum":["variable","lista","midnight","term","exactly","teller","fixedTerm","userSet","zeroInterest"],"description":"Which repayment rule priced this quote."},"aprPct":{"type":"number","description":"Sticker rate, APR %. Includes intrinsic yield and reward APR (same composition as `borrowAprShort` / `depositAprLong`); a one-time origination fee is NOT in it — the horizon model amortizes that separately."},"aprAtAmountPct":{"type":"number","nullable":true,"description":"The rate at the requested size. Null when no amount was supplied. A utilization pool re-prices its whole balance to the post-action rate; an order book fills cheapest-first, so this is the VWAP of the tiers consumed."},"rewardAprPct":{"type":"number","nullable":true,"description":"Reward emissions folded into the headline (positive = subsidising a borrow). Large emissions can push a borrow rate NEGATIVE."},"aprExRewardsPct":{"type":"number","nullable":true,"description":"The rate WITHOUT reward emissions — the structural, sustainable cost. Show it next to a reward-inflated headline; emissions are transient."},"effectiveAprPct":{"type":"number","nullable":true,"description":"Size-priced rate normalized to `horizonDays`. The ranking number."},"costPct":{"type":"number","nullable":true,"description":"Total cost over the horizon as a percent of principal."},"horizon":{"$ref":"#/components/schemas/ComparableRateHorizon"},"termId":{"type":"string","nullable":true},"durationDays":{"type":"number","nullable":true},"maturity":{"type":"number","nullable":true,"description":"Calendar maturity, unix seconds. Null for rolling menus and variable pools."},"termDays":{"type":"number","nullable":true,"description":"Remaining term in days for a position opened now."},"obtainable":{"type":"boolean","description":"Can this rate be taken right now? False for a Term repo between auction rounds, whose rate card still quotes the last round’s clearing rate."},"obtainableReason":{"type":"string","nullable":true},"quoteBasis":{"type":"string","enum":["live","last-clearing"],"description":"`last-clearing` marks a historical print, not an obtainable quote — never render it as an actionable rate."},"depth":{"$ref":"#/components/schemas/ComparableRateDepth"},"collateral":{"$ref":"#/components/schemas/ComparableRateAsset"},"debt":{"$ref":"#/components/schemas/ComparableRateAsset"},"maxLeverage":{"type":"number","nullable":true},"ltv":{"type":"number","nullable":true},"risk":{"type":"object","properties":{"configScore":{"type":"number","nullable":true},"maxTokenScore":{"type":"number","nullable":true}}}}},"ComparableRatesResponse":{"type":"object","properties":{"side":{"type":"string","enum":["borrow","supply"]},"horizonDays":{"type":"number"},"amount":{"type":"number"},"amountUsd":{"type":"number"},"chainIds":{"type":"array","items":{"type":"string"}},"scanned":{"type":"integer","description":"Pair rows considered before ranking."},"truncated":{"type":"boolean","description":"The candidate guard cap bound, so ranking saw only the deepest rows — narrow the filter. Never a silent trim."},"available":{"type":"integer","description":"Distinct comparables that existed before `limit` was applied."},"liquidityFloorUsd":{"type":"number","description":"Venues below this USD depth were dropped as not comparable (0 = no floor applied)."},"droppedIlliquid":{"type":"integer","description":"How many venues the depth band removed. An opinionated filter must never be silent — surface this rather than implying nothing else exists."},"droppedStale":{"type":"integer","description":"Venues excluded because their market data is older than `staleMaxHours` — an ingest lag, not an absent lender. Non-zero is the difference between \"no one else offers this pair\" and \"we cannot currently see who does\". `includeStale=true` ranks them anyway."},"staleMaxHours":{"type":"number","description":"The freshness window `droppedStale` was measured against."},"collateralBasis":{"type":"object","additionalProperties":{"type":"string"},"description":"The single collateral group each chain's rows were compared against when the caller pinned none, e.g. `{\"1\":\"ETH\"}`. Empty when a collateral was supplied, or on the supply side."},"reference":{"allOf":[{"$ref":"#/components/schemas/ComparableRate"}],"nullable":true,"description":"The venue the caller is already on (`referenceMarketUid`), pulled out of `items` so the UI can render \"you: X% · best: Y%\"."},"count":{"type":"integer"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ComparableRate"}}}},"SparklineRequest":{"type":"object","required":["currencies","quotes"],"properties":{"currencies":{"type":"array","items":{"type":"string"},"description":"Currency identifiers. Use shorthand (\"usd\", \"eth\") or \"{chainId}-{address}\" format."},"quotes":{"type":"array","items":{"type":"string"},"description":"Quote identifiers (same format as currencies)"},"windowHours":{"type":"number","default":24,"description":"Lookback window in hours"}}},"SparklinePoint":{"type":"object","properties":{"value":{"type":"number","description":"Price ratio (currency / quote)"},"time":{"type":"string","format":"date-time"}}},"Sparkline":{"type":"object","properties":{"currency":{"type":"string"},"quote":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/SparklinePoint"}}}},"SparklineResponse":{"type":"object","properties":{"windowHours":{"type":"number"},"count":{"type":"integer","description":"Number of non-empty sparkline pairs"},"result":{"type":"array","items":{"$ref":"#/components/schemas/Sparkline"}}}},"MetaLendingCompleteResponse":{"type":"object","properties":{"count":{"type":"integer"},"items":{"type":"object","additionalProperties":true}}},"UserPositionResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/LenderDataEntry"},"description":"Flat array of lender entries sorted by net worth (descending). Each entry fuses position data with aggregated summary metrics."},"summary":{"$ref":"#/components/schemas/PortfolioSummaryResponse"},"partial":{"type":"boolean","description":"Present and true when at least one lender could not be read in full (RPC error or reverted call). Totals are then a lower bound — a lender whose reads all failed is omitted rather than reported as an empty position."},"incompleteLenders":{"type":"array","items":{"type":"string"},"description":"Present only when `partial` is set: the `chainId:lender` pairs whose on-chain reads (partly) failed.","example":["1:AAVE_V3","1:COMPOUND_V3_USDC"]}},"description":"User lending positions as a flat array with portfolio summary and per-chain breakdowns."},"LenderDataEntry":{"type":"object","required":["lender","chainId","account","data","balanceData","aprData"],"properties":{"lender":{"type":"string","description":"Lender identifier","example":"AAVE_V3"},"chainId":{"type":"string","description":"Chain ID","example":"1"},"account":{"type":"string","description":"User account address","example":"0xbadA9c382165b31419F4CC0eDf0Fa84f80A3C8E5"},"data":{"type":"array","items":{"$ref":"#/components/schemas/UserSubAccount"},"description":"Sub-account position data"},"balanceData":{"$ref":"#/components/schemas/SummaryBalanceData"},"aprData":{"$ref":"#/components/schemas/SummaryAprData"},"leverage":{"type":"number","description":"Leverage ratio (deposits / nav)","example":2}},"description":"Fused lender entry combining sub-account position data with aggregated summary metrics."},"UserSubAccount":{"type":"object","required":["accountId","balanceData","aprData","positions","userConfig"],"properties":{"accountId":{"type":"string","description":"Sub-account identifier (e.g., \"0\" for default, NFT ID for Init)","example":"0"},"health":{"type":"number","nullable":true,"description":"Health factor (null if no debt). Values > 1 are healthy, < 1 at risk of liquidation.","example":1.85},"borrowCapacityUSD":{"type":"number","description":"Total USD borrowable while maintaining health >= 1","example":3000},"balanceData":{"$ref":"#/components/schemas/BalanceData"},"aprData":{"$ref":"#/components/schemas/AprData"},"positions":{"type":"array","items":{"$ref":"#/components/schemas/LendingPosition"},"description":"Individual asset positions in this sub-account"},"userConfig":{"$ref":"#/components/schemas/UserConfig"}},"description":"Position data for a single sub-account within a lender."},"BalanceData":{"type":"object","properties":{"deposits":{"type":"number","description":"Total deposits in USD","example":10000.5},"debt":{"type":"number","description":"Total debt in USD","example":5000.25},"adjustedDebt":{"type":"number","description":"Debt adjusted for borrow factors","example":5500},"collateral":{"type":"number","description":"Collateral value in USD","example":9000},"collateralAllActive":{"type":"number","description":"Collateral if all assets were enabled","example":10000.5},"borrowDiscountedCollateral":{"type":"number","description":"Collateral discounted by borrow factors","example":8000},"borrowDiscountedCollateralAllActive":{"type":"number","description":"Discounted collateral if all enabled","example":9000},"nav":{"type":"number","description":"Net asset value (deposits - debt)","example":5000.25},"deposits24h":{"type":"number","description":"Deposits 24h ago (for change calculation)","example":9800},"debt24h":{"type":"number","description":"Debt 24h ago","example":4900},"nav24h":{"type":"number","description":"NAV 24h ago","example":4900},"rewards":{"type":"array","nullable":true,"description":"Pending reward token claims. Each entry represents a single reward program.","items":{"$ref":"#/components/schemas/UserRewardEntry"}}},"description":"Aggregated balance data for a sub-account."},"AprData":{"type":"object","properties":{"apr":{"type":"number","description":"Net APR (deposit - borrow)","example":2.5},"depositApr":{"type":"number","description":"Weighted deposit APR","example":3.5},"borrowApr":{"type":"number","description":"Weighted borrow APR","example":5.2},"rewardApr":{"type":"number","description":"Total reward APR","example":1.2},"rewardDepositApr":{"type":"number","description":"Reward APR on deposits","example":0.8},"rewardBorrowApr":{"type":"number","description":"Reward APR on borrows","example":0.4},"intrinsicApr":{"type":"number","description":"Intrinsic yield APR (e.g., stETH staking)","example":0},"intrinsicDepositApr":{"type":"number","description":"Intrinsic yield APR portion from deposits","example":0},"intrinsicBorrowApr":{"type":"number","description":"Intrinsic yield APR portion from borrows","example":0},"rewards":{"type":"object","description":"Per-reward-token APR breakdown. Keys are reward token addresses.","additionalProperties":{"$ref":"#/components/schemas/RewardAprBreakdown"}}},"description":"APR breakdown for a sub-account."},"LendingPosition":{"type":"object","properties":{"marketUid":{"type":"string","description":"Unique market identifier (format: `{lender}:{chainId}:{address}`)","example":"AAVE_V3:1:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"},"deposits":{"type":"string","description":"Deposit amount in token units (wei)","example":"1000000000000000000"},"debt":{"type":"string","description":"Variable debt in token units","example":"0"},"debtStable":{"type":"string","description":"Stable debt in token units","example":"0"},"debtShares":{"type":"string","description":"Debt share amount (protocol-specific, present when the protocol uses share-based accounting)","example":"0"},"depositShares":{"type":"string","description":"Deposit share amount (protocol-specific, present when the protocol uses share-based accounting)","example":"0"},"depositsUSD":{"type":"number","description":"Deposits in USD (market price)","example":2500},"debtUSD":{"type":"number","description":"Variable debt in USD (market price)","example":0},"debtStableUSD":{"type":"number","description":"Stable debt in USD (market price)","example":0},"depositsUSDOracle":{"type":"number","description":"Deposits in USD using on-chain oracle price (used for risk/health calculations)","example":2510},"debtUSDOracle":{"type":"number","description":"Variable debt in USD using on-chain oracle price","example":0},"debtStableUSDOracle":{"type":"number","description":"Stable debt in USD using on-chain oracle price","example":0},"collateralEnabled":{"type":"boolean","description":"Whether this asset is enabled as collateral","example":true},"claimableRewards":{"type":"number","description":"Claimable rewards in USD","example":0.5},"withdrawable":{"type":"string","description":"Max tokens withdrawable while maintaining health >= 1. Equals full deposit balance if collateral is not enabled.","example":"0.5"},"borrowable":{"type":"string","description":"Max tokens borrowable against remaining credit line. Zero if borrowing is disabled, reserve is frozen, or debt is disabled for the active mode.","example":"100"},"underlyingInfo":{"$ref":"#/components/schemas/MarketUnderlyingInfo","description":"Underlying asset metadata, oracle price, and market prices for this position."},"loanId":{"type":"string","nullable":true,"description":"Lista DAO brokered markets only: identifies a single fixed-term loan (the broker posId; the dynamic/flex position uses `type(uint128).max`). Present on per-loan breakdown rows; absent on the aggregate debt row and on the shared collateral row. Pass to `/v1/actions/lending/repay?loanId=…`.","example":"450"},"term":{"$ref":"#/components/schemas/LoanTerm","nullable":true,"description":"Lista DAO brokered markets only: the fixed-term detail for this loan (rate, maturity, accrued interest, early-repay penalty). Present only on per-loan breakdown rows."}},"description":"Position data for a single asset within a lending protocol.\n\nFor **Lista DAO fixed-term (brokered)** markets a single market expands into several rows sharing the same `marketUid`:\n- one **aggregate debt** row (no `term`/`loanId`) where `debt` = the dynamic/variable loan and `debtStable` = the sum of fixed loans — use this for totals & health;\n- one **shared collateral** row (`collateralEnabled = true`, no `loanId`) backing every loan;\n- one **per-loan** row per open loan, each carrying `loanId` and a `term`. These are a breakdown of the aggregate — do not sum them into totals on top of the aggregate row."},"UserConfig":{"type":"object","properties":{"selectedMode":{"type":"string","description":"Mode/config key (e.g. e-mode category or vault address)","example":"0"},"id":{"type":"string","description":"Config identifier","example":"0"},"isWhitelisted":{"type":"boolean","description":"Whether user is whitelisted (for permissioned markets)"}},"description":"User configuration for a sub-account."},"SummaryBalanceData":{"type":"object","properties":{"deposits":{"type":"number","description":"Total deposits in USD","example":10000.5},"debt":{"type":"number","description":"Total debt in USD","example":5000.25},"collateral":{"type":"number","description":"Collateral value in USD","example":9000},"collateralAllActive":{"type":"number","description":"Collateral if all assets were enabled","example":10000.5},"nav":{"type":"number","description":"Net asset value (deposits - debt)","example":5000.25},"deposits24h":{"type":"number","description":"Deposits 24h ago","example":9800},"debt24h":{"type":"number","description":"Debt 24h ago","example":4900},"nav24h":{"type":"number","description":"NAV 24h ago","example":4900},"rewards":{"type":"array","nullable":true,"description":"Pending reward token claims. Each entry represents a single reward program.","items":{"$ref":"#/components/schemas/UserRewardEntry"}}},"description":"Summary-level balance data (without discounted/adjusted fields)."},"SummaryAprData":{"type":"object","properties":{"apr":{"type":"number","description":"Net APR (deposit - borrow)","example":2.5},"depositApr":{"type":"number","description":"Weighted deposit APR","example":3.5},"borrowApr":{"type":"number","description":"Weighted borrow APR","example":5.2},"rewardApr":{"type":"number","description":"Total reward APR","example":1.2},"rewardDepositApr":{"type":"number","description":"Reward APR on deposits","example":0.8},"rewardBorrowApr":{"type":"number","description":"Reward APR on borrows","example":0.4},"intrinsicApr":{"type":"number","description":"Intrinsic yield APR (e.g., stETH staking)","example":0},"intrinsicDepositApr":{"type":"number","description":"Intrinsic yield APR portion from deposits","example":0},"intrinsicBorrowApr":{"type":"number","description":"Intrinsic yield APR portion from borrows","example":0},"rewards":{"type":"object","description":"Per-reward-token APR breakdown. Keys are reward token addresses.","additionalProperties":{"$ref":"#/components/schemas/RewardAprBreakdown"}}},"description":"Summary-level APR breakdown."},"PortfolioTotals":{"type":"object","properties":{"balanceData":{"$ref":"#/components/schemas/SummaryBalanceData"},"aprData":{"$ref":"#/components/schemas/SummaryAprData"},"leverage":{"type":"number","description":"Overall leverage ratio (deposits / nav)","example":2},"activeLenders":{"type":"integer","description":"Number of lenders with positions","example":3},"activeChains":{"type":"integer","description":"Number of chains with positions","example":2}},"description":"Aggregated portfolio totals across all chains and lenders."},"PortfolioSummaryResponse":{"allOf":[{"$ref":"#/components/schemas/PortfolioTotals"}],"type":"object","properties":{"chains":{"type":"array","items":{"$ref":"#/components/schemas/ChainSummary"},"description":"Per-chain totals. Per-lender data is fused into the top-level items array."}},"description":"Portfolio-wide totals with per-chain breakdowns. Per-lender summaries are fused into each LenderDataEntry in the items array."},"ChainSummary":{"type":"object","properties":{"chainId":{"type":"string","description":"Chain ID","example":"1"},"totalDepositsUSD":{"type":"number","description":"Total deposits on this chain in USD","example":20000},"totalDebtUSD":{"type":"number","description":"Total debt on this chain in USD","example":10000},"netWorth":{"type":"number","description":"Net worth on this chain","example":10000},"lenderCount":{"type":"integer","description":"Number of active lenders on this chain","example":2}},"description":"Aggregated totals for a single chain."},"RpcCallItem":{"type":"object","description":"A JSON-RPC call object to be executed against an EVM node.","required":["method","params"],"properties":{"method":{"type":"string","description":"JSON-RPC method name","example":"eth_call"},"params":{"type":"array","description":"JSON-RPC parameters (call object and block tag)","items":{}}}},"RpcCallResponse":{"type":"object","properties":{"data":{"type":"object","required":["rpcCallId","rpcCalls"],"properties":{"rpcCallId":{"type":"string","format":"uuid","description":"Unique ID referencing the server-side cached context. Pass this to /parse together with the raw responses. Valid for 5 minutes."},"rpcCalls":{"type":"array","description":"Ordered list of JSON-RPC calls to execute against the target chain RPC. Each call uses multicall3 aggregate3.","items":{"$ref":"#/components/schemas/RpcCallItem"}}}}}},"ParseUserDataRequest":{"type":"object","required":["rpcCallId","rawResponses"],"properties":{"rpcCallId":{"type":"string","format":"uuid","description":"The rpcCallId returned by /rpc-call."},"rawResponses":{"type":"array","description":"Raw JSON-RPC response results in the same order as the rpcCalls array. Each entry is the hex-encoded result of the corresponding multicall3 aggregate3 call.","items":{"type":"object","description":"Raw JSON-RPC response object","properties":{"result":{"type":"string","description":"Hex-encoded result data"}}}}}},"ParseTokenBalancesRequest":{"type":"object","required":["rpcCallId","rawResponses"],"properties":{"rpcCallId":{"type":"string","format":"uuid","description":"The rpcCallId returned by /token/balances/rpc-call."},"rawResponses":{"type":"array","description":"Array containing the raw hex-encoded result from the eth_call. Typically a single-element array.","items":{"type":"string","description":"Hex-encoded result string (0x-prefixed)"}}}},"TokenListResponse":{"type":"object","properties":{"chainId":{"type":"string","example":"1"},"count":{"type":"integer","description":"Total number of tokens"},"tokens":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/TokenMeta"},"description":"Map of token address (lowercase) → token metadata"}}},"TokenMeta":{"type":"object","properties":{"symbol":{"type":"string","example":"USDC"},"name":{"type":"string","example":"USD Coin"},"decimals":{"type":"integer","example":6},"assetGroup":{"type":"string","example":"USDC"},"logoURI":{"type":"string","description":"Optional logo URL"}}},"TokenBalance":{"type":"object","properties":{"address":{"type":"string","description":"Token contract address (zeroAddress for native)","example":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"},"symbol":{"type":"string","example":"USDC"},"name":{"type":"string","example":"USD Coin"},"decimals":{"type":"integer","example":6},"balanceRaw":{"type":"string","description":"Raw balance in smallest unit (wei)","example":"1000000"},"balance":{"type":"string","description":"Formatted balance as decimal string","example":"1.0"},"priceUSD":{"type":"number","description":"Per-unit USD price of the token","example":1},"balanceUSD":{"type":"number","description":"Balance value in USD","example":1}}},"TokenPricesResponse":{"type":"object","properties":{"chainId":{"type":"string","example":"1"},"count":{"type":"integer","description":"Number of resolved assets"},"items":{"type":"object","additionalProperties":{"type":"number"},"description":"Map of lowercase address → USD price","example":{"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48":1,"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2":3500.12}}}},"TokenBalancesResponse":{"type":"object","properties":{"chainId":{"type":"string","example":"1"},"account":{"type":"string","description":"Lowercase account address","example":"0xbada9c382165b31419f4cc0edf0fa84f80a3c8e5"},"count":{"type":"integer","description":"Number of balance entries"},"items":{"type":"array","items":{"$ref":"#/components/schemas/TokenBalance"}}}},"AvailableAsset":{"type":"object","properties":{"address":{"type":"string","description":"Token contract address","example":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"},"chain_id":{"type":"integer","example":1},"symbol":{"type":"string","example":"USDC"},"name":{"type":"string","example":"USD Coin"}},"additionalProperties":true},"TokenAvailableResponse":{"type":"object","properties":{"count":{"type":"integer","description":"Number of available assets"},"items":{"type":"array","items":{"$ref":"#/components/schemas/AvailableAsset"}}}},"NextAccountResponse":{"type":"object","required":["accountType","nextAccountId","activeAccountIds","accountIdRange","createHint"],"properties":{"accountType":{"type":"string","enum":["SELECT","AUTOGEN"],"description":"SELECT — integrator picks an ID from the range (Euler V2). AUTOGEN — ID is generated on-chain; omit the param to create (Init Capital)."},"nextAccountId":{"type":"string","description":"For SELECT: lowest unused account ID. For AUTOGEN: preview of the on-chain generated ID.","example":"1"},"activeAccountIds":{"type":"array","items":{"type":"string"},"description":"Currently active account IDs for this owner. May be empty for AUTOGEN protocols.","example":["0","3","7"]},"accountIdRange":{"type":"array","items":{"type":"string"},"minItems":2,"maxItems":2,"description":"Inclusive [min, max] range of valid account IDs.","example":["0","255"]},"createHint":{"type":"string","description":"Human-readable instructions for integrators on how to create a new sub-account."}}},"LenderIdsResponse":{"type":"array","description":"Sorted list of supported lender protocol identifiers.","items":{"type":"string"},"example":["AAVE_V2","AAVE_V3","COMPOUND_V2","COMPOUND_V3_USDC","EULER_V2","INIT","LISTA","MORPHO","SPARK","VENUS"]},"MidnightMakeInputs":{"type":"object","description":"Deterministic offer inputs. Returned verbatim by `GET /v1/actions/midnight/make`; POST them back **unchanged** to `/v1/actions/midnight/finalize` alongside the signature so the worker rebuilds the identical offer tree. Any tampering fails on-chain ratification.","properties":{"chainId":{"type":"string","example":"8453"},"lender":{"type":"string","description":"`MORPHO_MIDNIGHT_<id>` lender key.","example":"MORPHO_MIDNIGHT_0xABC…"},"buy":{"type":"boolean","description":"`true` = maker BUYS units (a **lend** offer / bid); `false` = maker SELLS units (a **borrow** offer / ask)."},"tick":{"type":"string","description":"Order-book tick the APR snapped to."},"start":{"type":"string","example":"0"},"expiry":{"type":"string","description":"Offer expiry, unix seconds."},"maxAssets":{"type":"string","description":"Maker-side size in loan-token units."},"maker":{"type":"string","description":"Maker (offer owner) address."}}},"MidnightMakeResponse":{"type":"object","description":"Everything the frontend needs to sign and publish a maker offer (step 1 of the make flow). `actions` is `null` — the publish transaction is produced by `/finalize` after signing.","properties":{"typedData":{"type":"object","additionalProperties":true,"description":"EIP-712 typed-data to sign with `wallet.signTypedData` (bigints serialized as strings — sign the response as-is)."},"inputs":{"$ref":"#/components/schemas/MidnightMakeInputs"},"authorization":{"type":"object","description":"One-time `setIsAuthorized(ecrecoverRatifier, true)` transaction on the Midnight core. Send FIRST, only if the maker has not authorized the ratifier yet (read `isAuthorized` on-chain).","properties":{"to":{"type":"string"},"data":{"type":"string"},"value":{"type":"string"},"ratifier":{"type":"string"}}},"aprPctSnapped":{"type":"number","description":"The exact APR (percent) after tick-snapping — what the offer will actually quote once posted.","example":3.49},"maturity":{"type":"number","description":"Market maturity, unix seconds."}}},"MidnightFinalizeRequest":{"type":"object","required":["inputs","signature"],"description":"Body for `POST /v1/actions/midnight/finalize`: the `inputs` echoed from `/make` plus the maker’s EIP-712 signature over `typedData`.","properties":{"inputs":{"$ref":"#/components/schemas/MidnightMakeInputs"},"signature":{"type":"string","description":"Maker signature over `typedData` (0x hex)."}}},"TermOfferSubmission":{"type":"object","description":"A sealed lend offer for a Term auction.","required":["offeror","offerPriceHash","amount"],"properties":{"id":{"type":"string","description":"bytes32 — omit / zero for a new offer (the locker assigns it)."},"offeror":{"type":"string","description":"Offer owner address."},"offerPriceHash":{"type":"string","description":"bytes32 commitment = `keccak(price, nonce)`, computed client-side; revealed later."},"amount":{"type":"string","description":"Offer size in purchase-token units (integer wei)."},"purchaseToken":{"type":"string","description":"Optional; defaults to the repo's purchase token."}}},"TermBidSubmission":{"type":"object","description":"A sealed borrow bid for a Term auction (escrows collateral).","required":["bidder","bidPriceHash","amount","collateralAmounts","collateralTokens"],"properties":{"id":{"type":"string","description":"bytes32 — omit / zero for a new bid."},"bidder":{"type":"string","description":"Bid owner address."},"bidPriceHash":{"type":"string","description":"bytes32 commitment = `keccak(price, nonce)`, computed client-side."},"amount":{"type":"string","description":"Borrow size in purchase-token units (integer wei)."},"collateralAmounts":{"type":"array","items":{"type":"string"},"description":"Collateral amounts to escrow, aligned with `collateralTokens`."},"collateralTokens":{"type":"array","items":{"type":"string"},"description":"Collateral token addresses."},"purchaseToken":{"type":"string","description":"Optional; defaults to the repo's purchase token."}}},"TermOfferRequest":{"type":"object","required":["chainId","lender","submissions"],"properties":{"chainId":{"type":"string","example":"1"},"lender":{"type":"string","example":"TERM_FINANCE_0xABC…"},"submissions":{"type":"array","items":{"$ref":"#/components/schemas/TermOfferSubmission"}},"referral":{"type":"string","description":"Optional referral address."}}},"TermBidRequest":{"type":"object","required":["chainId","lender","submissions"],"properties":{"chainId":{"type":"string","example":"1"},"lender":{"type":"string","example":"TERM_FINANCE_0xABC…"},"submissions":{"type":"array","items":{"$ref":"#/components/schemas/TermBidSubmission"}},"referral":{"type":"string","description":"Optional referral address."}}},"TermRevealRequest":{"type":"object","description":"Reveal previously-locked offers or bids (opens the sealed price + nonce).","required":["chainId","lender","ids","prices","nonces"],"properties":{"chainId":{"type":"string","example":"1"},"lender":{"type":"string","example":"TERM_FINANCE_0xABC…"},"ids":{"type":"array","items":{"type":"string"},"description":"bytes32 offer/bid ids."},"prices":{"type":"array","items":{"type":"string"},"description":"Revealed prices (integer)."},"nonces":{"type":"array","items":{"type":"string"},"description":"Revealed nonces."}}},"UnifiedUserOrder":{"type":"object","description":"One of a user's own order-book / auction orders, normalized across providers (Midnight maker offers, Term secondary listings, Term auction submissions). `cancel` is a self-describing action the UI can fire directly.","properties":{"id":{"type":"string","description":"Provider order id — the cancel target (Midnight offer root / Term listingId / auction submission id)."},"lender":{"type":"string","example":"TERM_FINANCE_0xABC…"},"chainId":{"type":"string","example":"1"},"kind":{"type":"string","enum":["maker-offer","listing","auction-offer","auction-bid"],"description":"`maker-offer` (Midnight signed limit offer), `listing` (Term secondary repo-token listing), `auction-offer`/`auction-bid` (Term primary sealed-bid submission)."},"side":{"type":"string","enum":["lend","borrow"]},"amount":{"type":"string","description":"Order size, loan-token base units."},"assets":{"type":"number","nullable":true,"description":"Size decimal-scaled to loan-token assets, or null."},"aprPct":{"type":"number","nullable":true,"description":"Annualized rate in percent, or null while sealed."},"status":{"type":"string","enum":["open","sealed","revealed","filled","closed"],"description":"`open` = cancellable now (maker offer / listing); `sealed`/`revealed` = auction lifecycle; `filled` = assigned at clearing; `closed` = complete or cancelled."},"filledAmount":{"type":"string","description":"Amount assigned at clearing (auctions), base units."},"maturity":{"type":"number","description":"Market/repo maturity, unix seconds."},"expiry":{"type":"number","description":"Maker-offer expiry, unix seconds (Midnight)."},"revealTime":{"type":"number","description":"Auction reveal window opens, unix seconds (Term)."},"auctionEndTime":{"type":"number","description":"Auction closes / clears, unix seconds (Term)."},"cancel":{"type":"object","nullable":true,"description":"Self-describing cancel/unlock action — omitted when not cancellable. Fetch `path`+`query` to build the cancel transaction.","properties":{"method":{"type":"string","enum":["GET","POST"]},"path":{"type":"string","example":"/v1/actions/term/unlock-offers"},"query":{"type":"object","additionalProperties":{"type":"string"}}}}}},"UnifiedOrdersResponse":{"type":"object","description":"A user's own orders across order-book / auction lenders.","properties":{"orders":{"type":"array","items":{"$ref":"#/components/schemas/UnifiedUserOrder"}}}}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Optional. Without a key, requests are limited to 10 per 15 minutes. Get your key from https://auth.1delta.io/"}}}}