# Midnight Scan > Live explorer for Morpho Midnight fixed-maturity lending on Base, Ethereum, Arc, Tempo, and Robinhood Chain: markets, transactions, positions and the mempool. Morpho Midnight is a lending protocol where every loan has a fixed end date. Lenders and borrowers post offers with the terms they want, and those offers are published on-chain; this site calls that stream the mempool. When someone accepts an offer, the loan is made: the borrower receives less today than they repay at the end date, and that difference is the interest. Loan sizes are shown as the amount due at the end date. Every amount on this site comes from the blockchain itself; dollar values come from DeFiLlama prices. Midnight Scan is built by Tenor Labs. Tenor is building a global lending platform on top of Morpho Midnight: a turnkey platform that gives lenders and borrowers access to non-custodial lending markets on Midnight. ## Pages - [Overview](https://midnightscan.org/): Live explorer for Morpho Midnight fixed-maturity lending on Base, Ethereum, Arc, Tempo, and Robinhood Chain: markets, transactions, positions and the mempool. - [Activity](https://midnightscan.org/activity): Live feed of Morpho Midnight events on Base, Ethereum, Arc, Tempo, and Robinhood Chain: takes, repays, withdrawals, collateral moves and liquidations. - [Mempool](https://midnightscan.org/mempool): The Morpho Midnight offer mempool, live: every offer broadcast on Base, Ethereum, Arc, Tempo, and Robinhood Chain, decoded with its side, price and status. - [Markets](https://midnightscan.org/markets): Every Morpho Midnight lending market on Base, Ethereum, Arc, Tempo, and Robinhood Chain: loan and collateral tokens, maturities, borrowed, takes and users. - [Users](https://midnightscan.org/users): Morpho Midnight borrowers and lenders on Base, Ethereum, Arc, Tempo, and Robinhood Chain: debt, collateral, balances, positions and next maturity. - [Risk](https://midnightscan.org/risk): Morpho Midnight borrow positions closest to liquidation: LTV against LLTV, debt, collateral value and liquidation prices from live oracle reads. ## Entity URLs Every entity has its own page. Hex ids are lowercase; `chainId` is one of the ids under Chains. - Transaction: `https://midnightscan.org/tx/{chainId}/{txHash}` - Market, one maturity: `https://midnightscan.org/market/{chainId}/{marketId}` - Tenor market, every maturity sharing a loan token, collateral and gates: `https://midnightscan.org/markets/{chainId}/{tenorMarketId}` - Offer: `https://midnightscan.org/offer/{chainId}/{offerHash}` - Mempool broadcast: `https://midnightscan.org/message/{chainId}/{eventId}` - Address, across every chain: `https://midnightscan.org/address/{address}` - Tenor vault: `https://midnightscan.org/tools/vaults/{chainId}/{address}` ## Chains - Base: chainId 8453 - Ethereum: chainId 1 - Arc: chainId 5042 - Tempo: chainId 4217 - Robinhood Chain: chainId 4663 ## Notes - Pages render live data in the browser and refresh with every indexed block. A fetch without JavaScript gets each page's title, description and heading, not its tables — use the Markdown twin or the REST API below for the data. ## Machine-readable surfaces Every surface below returns the SAME facts as the page it mirrors, as of the indexed block it states (`Indexed through` in Markdown, `meta.indexed` in JSON). ### Markdown twins Append `.md` to a page path (`https://midnightscan.org/markets.md`; the home page is `https://midnightscan.org/index.md`), or request the page URL with `Accept: text/markdown`. The response is `text/markdown`, starts with a provenance list (source, generated, indexed-through, machine-readable siblings), ends with the unit conventions, and carries `Link: ; rel="canonical"` and `Vary: Accept`. Pages with a twin: | Twin | Documents | | --- | --- | | `https://midnightscan.org/index.md` | Overview: protocol totals, largest markets, latest activity and broadcasts | | `https://midnightscan.org/markets.md` | Every market, grouped by Tenor market (loan token, collateral, gates) | | `https://midnightscan.org/market/{chainId}/{marketId}.md` | One Midnight market (one maturity): terms, totals, fees, takes, positions, live offers | | `https://midnightscan.org/markets/{chainId}/{tenorMarketId}.md` | One Tenor market: every maturity sharing a loan token, collateral set and gates | | `https://midnightscan.org/address/{address}.md` | One address across every chain: borrow and lend positions, offers, fills, liquidations | | `https://midnightscan.org/mempool.md` | The offer mempool: latest broadcasts with their decoded offers | | `https://midnightscan.org/message/{chainId}/{eventId}.md` | One mempool broadcast: the offers inside with side, price, APR, status | | `https://midnightscan.org/offer/{chainId}/{offerHash}.md` | One offer: terms, maker, market, broadcast history, fills and consumption | | `https://midnightscan.org/tx/{chainId}/{txHash}.md` | One transaction: the Midnight events it emitted and the position/market state diff | | `https://midnightscan.org/activity.md` | Latest protocol events: takes, repays, withdrawals, collateral, liquidations | | `https://midnightscan.org/users.md` | Borrowers and lenders ranked by debt and credit, with collateral and next maturity | | `https://midnightscan.org/risk.md` | Borrow positions closest to liquidation: LTV against LLTV | | `https://midnightscan.org/tools/vaults.md` | Tenor vaults: assets, supply, share price, holders | | `https://midnightscan.org/tools/vaults/{chainId}/{address}.md` | One Tenor vault: totals, share price and holders | ### REST API JSON under `https://midnightscan.org/api/v1`; every response is an envelope `{ data, meta }` where `meta` carries `generatedAt`, `source`, per-chain `indexed` stamps and `encoding: "bigint-as-decimal-string"`. Errors are `application/problem+json` (400 bad input, 404 unknown entity or chain, 502 indexer unavailable). CORS is open to any origin; responses cache for ~10s. OpenAPI 3.1: `https://midnightscan.org/api/v1/openapi.json`. | Endpoint | Returns | | --- | --- | | `https://midnightscan.org/api/v1` | Index of every endpoint below | | `https://midnightscan.org/api/v1/openapi.json` | OpenAPI 3.1 for the whole API | | `https://midnightscan.org/api/v1/chains` | The chain registry: ids, names, contract addresses, start blocks | | `https://midnightscan.org/api/v1/status` | Indexed-through block and time per chain | | `https://midnightscan.org/api/v1/overview` | The home page: protocol totals, largest markets, latest activity and broadcasts | | `https://midnightscan.org/api/v1/markets` | Every Midnight market, one per maturity (`chainId`, `loanToken`, `status`, `limit`) | | `https://midnightscan.org/api/v1/markets/{chainId}/{marketId}` | One market: terms, totals, fees, collateral, feeds, positions, live offers | | `https://midnightscan.org/api/v1/tenor-markets` | Markets grouped by Tenor market id, as on /markets (`chainId`, `loanToken`, `collateral`, `minBorrowedUsd`, `status`, `offset`) | | `https://midnightscan.org/api/v1/tenor-markets/{chainId}/{tenorMarketId}` | One Tenor market and its maturities | | `https://midnightscan.org/api/v1/address/{address}` | One address across every chain: positions, offers, activity summary | | `https://midnightscan.org/api/v1/address/{address}/positions` | That address's borrow and lend positions with collateral | | `https://midnightscan.org/api/v1/address/{address}/activity` | One history feed of that address, paged (`feed`, `chainId`, `limit`, `before`) | | `https://midnightscan.org/api/v1/tx/{chainId}/{hash}` | One transaction's Midnight/Mempool events and state diff | | `https://midnightscan.org/api/v1/activity` | Latest protocol events (`type`, `chainId`, `marketId`, `address`, `minUsd`, `limit`, `before`) | | `https://midnightscan.org/api/v1/users` | Borrowers and lenders ranked by debt and credit | | `https://midnightscan.org/api/v1/risk` | Borrow positions closest to liquidation: LTV against LLTV | | `https://midnightscan.org/api/v1/mempool/broadcasts` | Latest offer broadcasts with decode status and offer counts | | `https://midnightscan.org/api/v1/mempool/offers` | The order book: offers takeable now, best quote first, from the newest broadcasts (`status`, `kind`, `chainId`, `marketId`, `loanToken`, `maker`) | | `https://midnightscan.org/api/v1/broadcasts/{chainId}/{eventId}` | One broadcast and the offers inside it | | `https://midnightscan.org/api/v1/offers/{chainId}/{offerHash}` | One offer: terms, maker, fills, consumption | | `https://midnightscan.org/api/v1/vaults` | Tenor vaults: assets, supply, share price | | `https://midnightscan.org/api/v1/vaults/{chainId}/{address}` | One vault: totals, share price, holders | | `https://midnightscan.org/api/v1/search?q=` | Resolve any identifier to its page(s) — JSON form of /go | ### MCP server `POST https://midnightscan.org/mcp` — Model Context Protocol over Streamable HTTP, stateless JSON-RPC 2.0 (no session to keep; add it to a client as a URL server). Tools: `search`, `get_overview`, `list_chains`, `get_status`, `list_tenor_markets`, `list_markets`, `get_market`, `get_tenor_market`, `get_address`, `get_address_positions`, `get_transaction`, `list_activity`, `list_users`, `list_risk`, `list_mempool_offers`, `list_broadcasts`, `get_broadcast`, `get_offer`, `list_vaults`, `get_vault`. Each tool returns the page's Markdown plus the same facts as structured data. Resources: `midnightscan://docs/.md` (the documents under /docs), `midnightscan://llms.txt`, the OpenAPI document. ### Resolver `https://midnightscan.org/go?q=` + anything — an address, a 32-byte id (tx hash, market id, Tenor market id, offer hash, vault market id), a block number, an ENS name, a site URL, or `{chainId}:{id}` to scope one chain. One match → 302 to the page; several → 300 with a Markdown (or JSON, `?format=json`) list; none → 404 explaining the accepted forms. `https://midnightscan.org/api/v1/search?q=` returns the same resolution as JSON — 200 even when nothing matches, 400 only for an empty `q`. ### Documents `https://midnightscan.org/docs` indexes the reference documents, each served as Markdown: - [Glossary](https://midnightscan.org/docs/glossary.md): Every protocol and explorer term — units, ticks, price, implied APR, LLTV, lossFactor, the three market ids, offerHash — with its scale and where it appears. - [Architecture & protocol reference](https://midnightscan.org/docs/architecture.md): Data flow from chain to page, the protocol model handlers encode (units, buyer = lender, ticks, market ids, offer death, maturity, lossFactor) and the handler invariants. - [Mempool offer wire format](https://midnightscan.org/docs/decoder.md): Spec of record for the offer-blob encoding broadcast through the Mempool contract: version byte, gzip envelope, the Offer struct, resource limits, derived hashes. - [Contract ground truth](https://midnightscan.org/docs/contracts.md): Source repositories, verified deployed addresses per chain, the Tenor vault registry, router attribution and the event → handler → table catalog. - [Machine-readable surfaces](https://midnightscan.org/docs/api.md): How to read the explorer as a program or a model: Markdown twins, the REST API and its envelope, OpenAPI, the MCP server, the resolver and llms.txt. `https://midnightscan.org/llms-full.txt` is this file followed by the glossary, architecture, decoder and API documents inlined — one fetch for the whole reference. ## Unit conventions Read these before interpreting any number. The protocol counts in units of the LOAN token, at maturity. | Quantity | Unit / scale | | --- | --- | | Units — `credit`, `debt`, `totalUnits`, `withdrawable`, `units`, `maxUnits`, `repaidUnits`, `badDebt`, `unitsTaken` | 1 unit = 1 smallest unit of the market's loan token, due at settlement (face value at maturity). units↔assets is 1:1 everywhere except `take`, where a price < 1 applies — the discount IS the interest. | | Assets — `buyerAssets`, `sellerAssets`, `maxAssets` | Loan-token smallest units paid NOW: a take moves `units × price / 1e18`, rounded against the taker. A non-zero settlement fee is the gap between `buyerAssets` and `sellerAssets`, borne by the taker. | | Collateral — `balance`, `assets` (collateral_event), `seizedAssets`, `totalCollateral` | Smallest units of the COLLATERAL token named on the row (its own `decimals`). | | Price — `priceWad`, a tick's price | WAD: 1e18 = par (1.0). Always < 1e18 for a discounted loan. A take's price is `buyerAssets × 1e18 / units`. | | Implied APR | Display convention: `(1e18 − price) / price ÷ (ttm / 31536000)`, simple interest over the seconds left to maturity (`ttm`). Unbounded as ttm → 0 (shown as ∞ past 1000%). | | `lltv`, `liquidationCursor`, Blue `lltv` | WAD: 1e18 = 100%. | | `lossFactor`, `latestLossFactor` | Scale `uint128.max` (2^128 − 1 = 100% of lender value written off) — NOT WAD. Only moves on bad-debt liquidations. | | `continuousFee`, `continuousFeeCap`, `signedRate`, `limitRatePerSecond` | WAD PER SECOND. Multiply by 31536000 for the annual rate (`1585489599` → 5.00%/yr). | | `settlementFeeCbps` | 7 WAD fractions of units charged at settlement, for time-to-maturity 0/1/7/30/90/180/360 days — piecewise linear between breakpoints, flat beyond 360d, the 0d value post-maturity. (The contract stores cbp, 1 cbp = 1e-6 WAD = 0.0001%; its fee events carry the WAD value, which is what the column holds.) | | Oracle `price()` | Loan token per collateral token × 1e36, both tokens' decimals already folded in. Health: `Σ floor(floor(balance × price / 1e36) × lltv / 1e18) ≥ debt`. | | Timestamps — `maturity`, `timestamp`, `start`, `expiry`, `hourTs`, `updatedAt` | Unix seconds (UTC). `start = 0` means no start constraint. | | Block numbers | Plain digits, never grouped (48824989). | | Hex identifiers | Lowercase, `0x`-prefixed. Addresses 40 hex, ids and hashes 64 hex. | | Numbers in JSON | Every on-chain integer (bigint) is a decimal STRING — never parse one as a float. `chainId`, `decimals`, counts and `idx` are JSON numbers. | | USD | From DeFiLlama at generation time, the site's only price feed; a vault share is priced as assets-per-share × the underlying's price. Shown in parentheses after the token amount. | ## How ticks encode price An offer's `tick` encodes a zero-coupon PRICE, not a rate: ``` price(tick) = 1e18 / (1 + 1.005^(3372 − tick)) WAD, rounded to a 1e11 step, increasing in tick ``` - Range 0…6744: tick 3372 is a price of 0.5; tick 6744 is par (1e18, 0% APR). Ticks must be multiples of the market's `tickSpacing` (default 4). - The on-chain lookup table (TickLib.sol, vendored as `packages/shared/src/ticks-table.ts`) is authoritative — never recompute the exponential; the table and the contract agree bit-for-bit. - The APR a tick implies depends on the time left: the same tick is a higher APR the closer the maturity (see Implied APR above). - A RateRatifierV1 offer is the exception: its maker signed a per-second simple-interest `signedRate` (WAD/s) and its `tick` is unsigned filler the taker replaces, so `priceWad` is null and the APR is `signedRate × 31536000`. ## Data model Indexed tables, as they appear in the JSON surfaces (camelCase keys). Every primary key starts with `chainId`; every event row also carries `eventId` (opaque, unique per chain), `txHash`, `blockNumber`, `logIndex` where listed, `timestamp` (unix s) and, on Midnight events, `accountTag` (`coinbase` for an ERC-4337 Coinbase smart wallet, else null). Column lists are trimmed to what a reader needs; the schema of record is `apps/indexer/ponder.schema.ts`. ### `market` One row per Midnight market = one maturity. Live state, updated event by event. | Column | Unit / meaning | | --- | --- | | marketId | 32-byte id (CREATE2/SSTORE2 of the Market struct) | | loanToken | Address; `token` has its symbol/decimals | | maturity | Unix s — after it: no new debt, liquidation unconditional, lenders withdraw FCFS | | rcfThreshold | Loan-token smallest units; a protocol setting, not a balance | | enterGate, liquidatorGate | Gate contract addresses (part of the market's identity) | | totalUnits | Units outstanding = Σ position.debt + withdrawable | | withdrawable | Units lenders can withdraw now (funded only by repayments and liquidations) | | lossFactor | uint128.max scale; share of lender value written off | | continuousFee | WAD/s; continuousFeeCredit in loan-token units | | tickSpacing | Integer; valid ticks are its multiples | | settlementFeeCbps | 7 WAD fractions (ttm 0/1/7/30/90/180/360 d) | | takeCount, createdAt, createdAtBlock, createdTxHash | Count; unix s; block; hash | ### `collateral_config` One row per (market, collateral token), `idx` = on-chain order. | Column | Unit / meaning | | --- | --- | | token | Collateral token address | | lltv | WAD — liquidation LTV for this collateral in this market | | liquidationCursor | WAD | | oracle | Address; `price()` quotes loan per collateral × 1e36 | ### `position` One row per (market, user). A user never holds credit and debt at once in one market. | Column | Unit / meaning | | --- | --- | | user | Address | | credit | Units owed TO the user at maturity (lender side) | | debt | Units the user owes at maturity (borrower side) | | pendingFee | Units of continuous fee accrued against the credit | | updatedAt | Unix s | ### `position_collateral` One row per (market, user, collateral token). | Column | Unit / meaning | | --- | --- | | token | Collateral token | | balance | Collateral-token smallest units posted | ### `take` One row per Take event — a fill. Buyer = lender side, seller = borrower side. | Column | Unit / meaning | | --- | --- | | offerHash, offerIsBuy, maker, taker, caller | Hash; maker's side; addresses (`caller` = msg.sender into Midnight, i.e. the router) | | units | Units filled | | buyerAssets, sellerAssets | Loan-token units paid now: price = buyerAssets × 1e18 / units | | buyerCreditIncrease, buyerDebtDecrease, sellerCreditDecrease, sellerDebtIncrease | Units — the per-side splits; they classify the take (new-borrow / credit-resale / debt-transfer / unwind / mixed) | | group, consumed | Offer group (bytes32); absolute `consumed[maker][group]` after the take — units, or loan-token assets when the offer's cap is `maxAssets` (the cap's own denomination) | | receiver, payer | Where the buyer's assets went / came from (a callback when liquidity is sourced) | ### `repay · withdraw · collateral_event · liquidation` Typed event feeds (`activity` holds the generic row for every event). | Column | Unit / meaning | | --- | --- | | repay.onBehalf, repay.units | Borrower whose debt fell; units | | withdraw.onBehalf, withdraw.units, withdraw.pendingFeeDecrease | Lender whose credit fell; units; units of fee settled | | collateral_event.kind, token, assets, onBehalf, receiver | `supply` or `withdraw`; collateral token; its smallest units; owner; receiver (withdraw only) | | liquidation.caller, borrower, collateral, seizedAssets | Liquidator; borrower; collateral token; collateral smallest units seized | | liquidation.repaidUnits, badDebt, postMaturityMode | Units; units written off; whether the unconditional post-maturity path was used | | liquidation.latestLossFactor | uint128.max scale, after this liquidation | ### `activity` Every Midnight and Mempool event, one row — the unified feed behind /activity and /tx. | Column | Unit / meaning | | --- | --- | | eventName, contract | Event name; `Midnight` or `Mempool` | | marketId, user | Nullable — the subject when the event has one | | via | Router attribution: the event's `caller` (msg.sender into Midnight), else the tx `to` | | argsJson | Full decoded args, bigints as decimal strings | ### `mempool_message` One row per Mempool `Data(bytes)` broadcast (the raw blob is kept). | Column | Unit / meaning | | --- | --- | | sender, via | tx `from`; tx `to` (the Mempool itself = direct, else a router) | | rawData, byteLength | Hex blob (docs/decoder.md wire format); bytes | | decodeStatus, decodeError, offerCount | `decoded` / `undecodable`; reason; offers inside | | hasLend, hasBorrow, hasRepay, hasResell | Which offer kinds the broadcast carries | ### `mempool_offer` One row per decoded offer inside a broadcast. | Column | Unit / meaning | | --- | --- | | offerHash, offerIndex | `keccak256(abi.encode(offer))`; position in the broadcast | | marketId, loanToken, maturity | The market the offer is for (created on-chain or not) | | buy, reduceOnly | Maker's side and flag → kind: lend (buy), repay (buy+reduceOnly), borrow (sell), resell (sell+reduceOnly) | | maker, allowedTaker, ratifier, group | Addresses; `allowedTaker` null = anyone; group is the consumption key | | start, expiry | Unix s — takeable window (start 0 = none) | | tick, priceWad, signedRate | Tick; its WAD price (null for RateRatifierV1); WAD/s rate (RateRatifierV1 only) | | maxUnits, maxAssets | Cap — exactly one is non-zero; both in loan-token smallest units | | continuousFeeCap | WAD/s — the offer dies if the market's fee rises above it | | callback, callbackData | Settlement-liquidity callback (zero address = none); raw hex params | | offerJson | The whole offer + ratifierData, bigints as strings | ### `offer_fill` Cumulative fills per offerHash, written by Take (independent of the decoder). | Column | Unit / meaning | | --- | --- | | unitsTaken, takeCount, lastTakeAt | Units; count; unix s | ### `vault · vault_snapshot` Tenor ERC-4626 vaults, READ on-chain hourly (yield accrual emits nothing). | Column | Unit / meaning | | --- | --- | | address, asset | Vault (share token); underlying asset | | totalAssets, totalSupply | Asset smallest units; shares — share price = totalAssets / totalSupply | | hourTs | Unix s at the hour (snapshot rows) | ### `token · stat_snapshot` ERC-20 metadata; hourly per-token aggregates for charts. | Column | Unit / meaning | | --- | --- | | token.address, symbol, name, decimals | Metadata read once per token | | stat_snapshot.token, hourTs, totalCollateral, totalBorrowed | Token; unix s; collateral posted in that token (its units); debt outstanding with it as loan token (units) | Also indexed: `announced_market` (markets a broadcast named before any take created them), `group_consumption` (`consumed[maker][group]`, global per maker+group — not per market), `account_tag`, `token_stat`, `vault_holder`, `vault_event`, `ratifier_action`, `blue_rolling_action`, `blue_buy_callback(_position)`, `tenor_deployment`, `tenor_contract_action`, `config_event`, `fee_default`, `position_change`, `collateral_change`, `market_change`. ## Chain registry Generated from the registry the indexer and the pages read (`packages/shared/src/chains.ts`). Addresses are lowercase and PER CHAIN — the same address holds different contracts on different chains; never copy one across. Tempo and Robinhood Chain run Morpho's core alone (no Tenor contracts, no vaults). | chainId | Chain | Midnight | Mempool | Morpho Blue | Start block | Block explorer | Public RPC | | --- | --- | --- | --- | --- | --- | --- | --- | | 8453 | Base | `0xadedd8ab6de832766fedf0fac4992e5c4d3ea18a` | `0xdd6dce32e21f7b020898a8258da37355b4017993` | `0xbbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb` | 48286884 | https://basescan.org | https://mainnet.base.org | | 1 | Ethereum | `0x471686c42792f93528b000bef54bc10e3aa2045f` | `0xde2d62449301a09a51ebf9326ea60d2e8bf4a8f7` | `0xbbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb` | 25798183 | https://etherscan.io | https://ethereum-rpc.publicnode.com | | 5042 | Arc | `0x208786922be56fde2d1fa60e6b9ec5d723e8d7b0` | `0x26bded5fb01373ce875dea14e52799d04c839c1a` | `0x34cd04070dd72b14e241112f6d83812df5af7fcd` | 20320779 | https://explorer.arc.io | https://rpc.mainnet.arc.io | | 4217 | Tempo | `0x31ff800b0fa4aa42071d4ae9d0392e84f256d83d` | `0x947f74a8809a45edc03b6101bd40acdfbe97553a` | `0x10ee9aac980a180dd4dcfc96c746d60b0ea88f97` | 42413205 | https://explore.tempo.xyz | https://rpc.tempo.xyz | | 4663 | Robinhood Chain | `0x6120765ba5336150bbdddd0cd9108b5bfd369632` | `0xcef685d4796fa80f71a97e803d2c0b6719f1b4e2` | `0x9d53d5e3bd5e8d4cbfa6db1ca238aea02e651010` | 65366296 | https://robinhoodchain.blockscout.com | https://rpc.mainnet.chain.robinhood.com | | Chain | MigrationRatifier (Tenor) | BlueBuyCallback factory | BlueFallbackRolling | OracleWithValidation factory | MidnightAllowlistGate factory | VaultV2AllowlistGate factory | DelayedLiquidationGate factory | | --- | --- | --- | --- | --- | --- | --- | --- | | Base | `0xf96ec15ac7d0f8fa3560b6ddf9d1f9d54f31daed` | `0x7337f119eca028bd39e0e543cef71631d2333425` | `0x7bfa8207b818cc0e23a7abdcbe56572fabca831f` | `0xee80d70b5671e17e88e2654f613a288af5689412` | `0x508bc8b7abb0ee4c885acf7ac3d58941baf76f7c` | `0x73ef8184ceb6c1d668bcb04c41a264c9468ac670` | `0xbb91265fd479094d7f1b1b20af79a07442df9850` | | Ethereum | `0x4bf0ef9b8ff6535b371c2bad12da67dc59d62a5c` | `0x172d1fdc5f79bfe1ed46448f18541e591e5c93a7` | `0x6910e21c20a09bb3a2026a24585af1e63d769fe6` | — | `0x782b3770e209fbd1f14a233256ee01ff04d4db3a` | `0xf6732aa6eaaf3754ed0290beb5dce17d77abd802` | `0xd554bb25ee3314a0012a844ddbba53a62477e045` | | Arc | `0x6487784572adee9e8c3d9f592acda1cbe6333f48` | `0x9bd11e1ec7bf3520896f8e3e63d4b70f8d6d177e` | `0x405711971ef6be74114778cefba9a2f2e4648f3d` | — | `0xfaba21ddc6c79699499635a32f673c2cfbfdc5c5` | `0xeb23d45ac20f3eaa20db8c6af5f7ef33ebe1e9e9` | `0x5140fef83821a2cb7cfbe1ff8cfb29a11f6ad2c0` | | Tempo | — | `0x0b92694fa046303db9371fd0a5722e0f3dc2c323` | `0x9b28416022e883bcaf8dfb967e0de345f444658c` | — | — | — | — | | Robinhood Chain | — | `0x53cbcd884caba07762c72f43283d3fa72de42d4f` | `0x1d0a555d4fd62bbbeea9a441aa3d8fd7d4b42878` | — | — | — | — | ## Identifier patterns Several kinds of 32-byte id look alike; the resolver (`/go?q=`) tells them apart by lookup. Rules of thumb: | Identifier | Shape | Page | JSON | | --- | --- | --- | --- | | Address | `^0x[0-9a-f]{40}$` | `/address/{address}` (every chain) | `/api/v1/address/{address}` | | Transaction hash | `^0x[0-9a-f]{64}$` | `/tx/{chainId}/{hash}` | `/api/v1/tx/{chainId}/{hash}` | | Midnight market id (one maturity) | 64 hex — `keccak256(0xff ‖ midnight ‖ 0 ‖ keccak256(SSTORE2_PREFIX ‖ abi.encode(market)))`; low 20 bytes = the market record's address | `/market/{chainId}/{marketId}` | `/api/v1/markets/{chainId}/{marketId}` | | Tenor market id (maturity-agnostic family) | 64 hex — `keccak256(abi.encode(chainId, midnight, loanToken, collateralParams, rcfThreshold, enterGate, liquidatorGate))`; commits to its chain | `/markets/{chainId}/{tenorMarketId}` | `/api/v1/tenor-markets/{chainId}/{tenorMarketId}` | | Vault market id | `^0x[0-9a-f]{40}0{24}$` — a vault address over 12 zero bytes; no hash, no chain | `/tools/vaults/{chainId}/{vault}` when one live registered vault matches, else `/tools/market-id?id=` | `/api/v1/vaults/{chainId}/{address}` | | Offer hash | 64 hex — `keccak256(abi.encode(offer))` = `Take.offerHash` (NOT the EIP-712 hash a ratifier signs) | `/offer/{chainId}/{offerHash}` | `/api/v1/offers/{chainId}/{offerHash}` | | Morpho Blue market id | 64 hex — `keccak256(abi.encode(loanToken, collateralToken, oracle, irm, lltv))`; not indexed here | `/tools/market-id?id=` decodes it via Blue's `idToMarketParams` | — | | Broadcast (event id) | Opaque text (Ponder event id) — URL-encode it | `/message/{chainId}/{eventId}` | `/api/v1/broadcasts/{chainId}/{eventId}` | | Block number | `^\d+$` | No page — `/go` offers each chain's block explorer | — | | ENS name | `name.eth` | `/go?q=name.eth` → `/address/{address}` | `/api/v1/search?q=name.eth` | `chainId` is required wherever a path has one: a market id, offer hash, tx hash or event id is only meaningful on its chain, and the same vault address may exist on several. ## Example queries Concrete requests, using Base (8453) ids where an id is needed: | Want | Request | | --- | --- | | Every market, as Markdown | `curl -H 'Accept: text/markdown' https://midnightscan.org/markets` or `curl https://midnightscan.org/markets.md` | | One market's twin | `https://midnightscan.org/market/8453/{marketId}.md` | | One market as JSON | `https://midnightscan.org/api/v1/markets/8453/{marketId}` → `{ data: { marketId, loanToken, maturity, terms, totals: { totalUnits, withdrawable, lossFactor, … }, … }, meta: { indexed: [{ chainId, block, timestamp }] } }` | | Latest 20 takes on Ethereum | `https://midnightscan.org/api/v1/activity?chainId=1&type=take&limit=20` | | What an address holds | `https://midnightscan.org/api/v1/address/{address}/positions` | | Live offers in the mempool | `https://midnightscan.org/api/v1/mempool/offers?chainId=8453&limit=50` | | Jump to whatever an id is | `https://midnightscan.org/go?q={address | hash | block | name.eth}` → 302 | | Resolve without redirecting | `https://midnightscan.org/api/v1/search?q=8453:0x{64 hex}` | | The glossary | `https://midnightscan.org/docs/glossary.md` | MCP, one call (`POST https://midnightscan.org/mcp`, `Content-Type: application/json`, `Accept: application/json, text/event-stream`): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_market", "arguments": { "chainId": 8453, "marketId": "0x…" } } } ``` ## Crawling `https://midnightscan.org/robots.txt` allows the list and entity pages (bounded by real chain activity) and this file, `https://midnightscan.org/llms-full.txt`, `https://midnightscan.org/docs/` and `https://midnightscan.org/api/v1/openapi.json`. It disallows the unbounded or param-driven spaces — `/search`, `/address/` (any of 2^160 addresses renders), `/tools` (fans out to a rate-limited upstream API when rendered), `/go`, `/api/v1/` and `/mcp` (programmatic, not pages), `/md/` (the twins' internal handler paths — fetch `.md` instead) and `/sql/` (the indexer's query API). Agents are welcome on all of them; the disallow is about crawl budget, not access. Markdown twins carry `Link: rel="canonical"` to their HTML page, so indexing credit goes to the page. `https://midnightscan.org/sitemap.xml` lists the list pages, the documents and this file. ## Optional - [Tenor Labs](https://tenor.finance): builds and runs Midnight Scan - [Tenor Labs on X](https://x.com/TenorFinance)