Term Finance: an order book bolted onto an auction
Term Finance originates fixed-rate loans in a sealed-bid auction that runs on a schedule - so most of the time, on most of its markets, nobody can borrow at all. Terminal 1 is the answer: a signed limit-order book that settles into the exact same repo tokens. How both halves work, how they compare to Midnight, and what August's governance exploit did and did not touch.
Our piece on Midnight made the case that leaving the pool is the drastic step in on-chain lending. Term Finance left it in a different direction, and earlier: instead of replacing the pool with a continuous order book, it replaced the pool with an auction.
That choice is the whole protocol. It gives Term the cleanest price discovery of any fixed-rate market we index - one rate, found by the market, paid by everyone who clears - and it produces a problem no pooled lender ever has: for most of its life, a Term market cannot be borrowed at all. Terminal 1, the layer Term shipped on top, is the answer to exactly that problem. This piece is about both halves, how they fit together, and how the pair compares to Midnight's pure order book.
A loan that is a repurchase agreement
Term is modeled explicitly on tri-party repo, and the vocabulary is not decoration. A borrower sells a claim at a purchase price and agrees to buy it back at maturity at a repurchase price; the difference is the interest. There is no utilization curve anywhere in the system, no interest index, and no per-block accrual.
What a borrower owes is one number, fixed at the moment the auction clears - getBorrowerRepurchaseObligation() - and it never moves again. That single design decision propagates everywhere:
- The debt is a static face value. Not a principal plus accrued interest. A figure.
- Repaying early is strictly worse than holding to maturity. You can repay any amount at any time with no gate, but there is no rebate of unearned interest - you transfer the same face value on day one that you would on the last day. Exactly does the opposite and prices an early repay like a deposit, handing back a discount. Two fixed-rate protocols, opposite answers to the same question, which is exactly why the term sheet has to state it rather than assume it.
- Collateral is measured as a ratio, not an LTV. A maintenance ratio of
1.25e18means 125% collateral against the loan, so the comparable LTV is its reciprocal - 80%.
Clearing produces three artifacts, and understanding them is most of understanding Term. Collateral moves into a collateral manager. The lender receives repo tokens, a zero-coupon ERC-20 redeemable for a fixed value at maturity. A servicer tracks the repurchase obligation and accepts repayment through a window that opens at maturity and closes at endOfRepurchaseWindow.
The auction is an event, not a state
Rate discovery happens in a sealed-bid auction with a uniform clearing price. Borrowers submit hashed bids, lenders submit offers, and at the reveal the protocol finds the one rate that clears the market. Bidders above it get funded, lenders below it get filled, and everyone else is - in Term's own phrase - left on the table. Every winner transacts at the same rate: the one the market found, not the one they personally named.
The consequence is the part nobody advertises. Because origination is an event rather than a state, a Term market spends most of its life closed to new borrowing. When we first modeled Term's public data this produced a shape no pooled lender ever produces: a market with a live, quotable rate that you cannot actually borrow at, because the round in which that rate cleared ended days ago.
The lend side is not symmetric, and that asymmetry is real rather than an artifact of our modeling. A lender can always take exposure between rounds by buying repo tokens on Term's secondary listings book, so a closed auction leaves the market lend-only rather than inert. That same book is the only early exit a lender has: there is no redeem-before-maturity path, so getting out early is a market sale at a market discount - a price, not a protocol fee.
Terminal 1: the book that fills the gaps
Terminal 1 is an EIP-2535 diamond - 28 facets, Sourcify-verified, live on Ethereum and Arbitrum - that adds a signed limit-order market and a flash-loan execution engine on top of the protocol Term already had.
The critical design fact is that it adds no new market. An order fill mints exactly the same repo tokens, moves collateral into exactly the same collateral manager, and books the obligation against exactly the same servicer as an auction fill. From a data model's point of view Terminal 1 is a second origination path into markets that already exist - which is why integrating it extended our existing Term provider rather than adding a lender to the index.
Orders live off-chain in an open, unauthenticated REST store and are enforced on-chain at fill. A LimitLendOrder or LimitBorrowOrder is EIP-712 typed data naming the servicer, the amount, the offer rate, an optional pinned taker, an expiry and a salt. Anyone can then call settleLimitLend or settleLimitBorrow - the taker is simply msg.sender - and the diamond validates the whole thing itself: that the servicer belongs to an approved controller and is therefore a genuine Term market, that the order has not expired, that the market has not matured, that remaining capacity covers the fill. Partial fills are accounted; bulk cancellation works by raising a minimum salt.
Two details make this a better maker surface than most signed-order systems:
- The typed-data round-trip is optional.
setPreSignedLendOrderHashis a plain transaction that authorizes an order hash on-chain, so a maker who cannot produce an EIP-712 signature - a contract, a custody stack, an integration with no signing path - can still post. That single affordance has blocked us outright on other lenders. - Resting orders are not idle capital. Each order carries a
RetrieveFundsStruct, a just-in-time funding hook: a maker's liquidity can sit earning yield in Aave, Morpho or an ERC-4626 vault right up until someone fills them, at which point settlement pulls it out in the same transaction.
The result on our side is a fill-now rate that is executable as quoted rather than a historical clearing price, and gates that finally open between rounds. At integration, 22 of 24 mainnet repos carried standing borrow liquidity in the stretches the auction leaves empty.
The unit trap: a 360-day year
An order's offerRate is a 1e18 fraction annualized over a 360-day year - the money-market convention, and not the one our surfaces quote. The 365-day equivalent is offerRate / 1e16 x (365/360): about 1.4% relative, invisible on a screen and material in a comparison.
We deliberately apply the same untranslated treatment to fill-now rates and auction clearing rates, so the two numbers on one row stay comparable to each other. The conversion belongs at the boundary where Term rates meet everyone else's, not in the middle of a row. Every fixed-rate integration we have done has had exactly one of these traps - TermMax names its rate fields from the maker's perspective, so its lendApr is the taker's borrow rate - and none of them announce themselves. The number is plausible, in the right units, and wrong.
Carry without arbitrary calls
The second half of Terminal 1 is flashExecute: a flash loan wrapped around a list of actions, which is how leveraged fixed-rate carry gets built in one transaction - borrow, swap into a yield-bearing asset, lock it as collateral, sell the minted repo tokens to fund the loan.
What is unusual is the safety posture. The actions are not arbitrary calls. Each step's method must resolve to a selector the diamond itself registers, each hook facet generates its own calldata and its own preview, and every step is validated against balance snapshots with surplus refunded to the user as it goes. Swap legs route through an immutable Pendle router. There is no open call() anywhere in the pipeline - a materially more conservative posture than the arbitrary-router zaps that several looping protocols ship.
The whole plan can also be dry-run: quoteExecutionPlan is a view that back-propagates minimum outputs through the entire pipeline, which let us validate route construction against mainnet without modeling any of it ourselves. That view validates amounts, though - not solvency, allowances or state. Four bugs survived it and were caught only by a fork test that actually executed the pipeline, including the one that is obvious in hindsight: when order-book funding routes borrow proceeds through the taker's wallet and pulls them back in, the purchase-token approval has to cover borrow plus margin, not margin alone.
One architectural consequence decided our whole routing approach. flashExecute is strictly self-initiated - the acting user is always msg.sender, with no on-behalf variant - so our composer cannot drive it, and our composer is immutable, so no Term opcode can be added to it. That sounds like a loss and is not. It means the entire Term surface is EOA-direct: zero Solidity from us, and no standing delegation to any shared arbitrary-call contract. The user's wallet talks to Term's diamond, and we build the calldata.
Term against Midnight
Midnight is the other fixed-rate order-book lender we serve, and it is the natural comparison - our Term provider was cloned from the Midnight one, and the two now sit side by side in the same code with a parity test asserting their public-data shapes match. The shared shape hides how differently they are built.
| Term Finance | Morpho Midnight | |
|---|---|---|
| Origination | Sealed-bid auction, uniform clearing price - plus a limit-order book since Terminal 1 | Order book only; signed offers taken continuously |
| Rate encoding | offerRate, a 1e18 fraction on a 360-day year | Discrete ticks to a WAD discount price; APR over 365 days |
| Debt shape | Static face - a repurchase obligation fixed at clearing | Static face - a uint128 unit count, units 1:1 with assets |
| Early repay | Any amount, any time. Full face, no rebate | Full face, 1:1. Over-repay reverts on a uint128 underflow |
| Liquidation trigger | Price only - the maintenance collateral ratio | Price and time - being past due is itself the trigger |
| Lender exit | Sell the repo token on the secondary book - market discount | Sell units into the bid side - market discount |
| Ongoing fees | Servicing fee on the repo; borrowFee inside each order | Continuous fee (capped 1%/yr) + settlement fee, both lender-side |
| Maker flow | EIP-712 signature or an on-chain presigned hash | Sign an offer tree, publish the ratified payload to the mempool contract |
| Execution route | EOA-direct into Term's diamond; flashExecute is msg.sender-scoped | Through our composer, via dedicated Midnight opcodes |
| Bad debt | Over-collateralized; liquidation-only | Socialized to lenders via lossFactor |
Two rows deserve more than a cell.
Maturity means different things. Term treats an unpaid loan as an ordinary credit position: liquidation is price-triggered on the maintenance ratio, no late penalty is modeled anywhere, and settlement proceeds through the repurchase window. Midnight makes maturity itself a liquidation trigger - a past-due loan can be liquidated regardless of health or LTV, with a dedicated postMaturityMode flag in the core. A borrower carrying identical over-collateralized fixed-term debt on the two protocols faces genuinely different risk the day after maturity, and no floating-rate intuition prepares anyone for the Midnight version.
The execution route is what an integrator feels. Midnight's take path is expressible as composer opcodes, so a Midnight loop, a Midnight migration and a Midnight spot action all flow through the same machinery as Aave or Morpho, and every leg composes with everything else. Term cannot, so every Term flow is native: its own carry builder, its own quoting, its own gates - and its own limits. There is no Term repay hook, because unwinding Term debt (repay, unlock collateral, sell) is not expressible as a single flash pipeline; that exit stays a sequential two-step through direct routes.
The August exploit, and which layer failed
Two days after we finished assessing Terminal 1, an attacker took roughly $8.5M out of Term's vault products through their governance. We covered the mechanics in the Tuesday Brief of 25 August: about $951 of staked governance token carried 90.66% of the votes in a pool nobody else had staked in, the proposal sat through a six-day veto window with zero vetoes, and it removed the seven-day cooldown before moving the money. The timelock and the veto window both worked exactly as designed. They are only defences if somebody is watching.
What matters here is the boundary. The lending markets were not touched. Term Labs states its V1 and V2 contracts were not compromised and that direct borrowing and lending markets operated throughout - supply, repayment and liquidation all kept working, and the last affected fixed-rate position was recovered by 25 August. Yearn separately confirmed the vector was Term's custom governance wrapper, not standard Yearn vault deployments. Everything described above - the auctions, the repo tokens, the servicer, the collateral manager, the Terminal 1 order book - was outside the blast radius. Meta Vaults are permanently closed to deposits, with withdrawals open.
That separation is the design working, and it is worth being precise about why it held: auction operation sits with a manager role and risk parameters with a governor role, while the vault DAO's votes reached only vault configuration. The failure was contained to the one domain in which a three-figure stake was a supermajority.
Where it does reach an integrator
"Core lending is unaffected" is not the same as "nothing changed for anyone building on it." Term's vaults were also market makers. Their liquidity is auto-quoted into the Terminal 1 order store as synthetic orders, and in the 30-day window we measured, 59 of 82 mainnet order fills were synthetic - vault-supplied - against 23 organic limit fills. Our own carry builder's strategy-funding path likewise sells minted repo tokens into a Term strategy vault, resolved by finding one that already holds the repo token.
With Meta Vaults closed and affected strategies shut down, the honest read is that the book's depth and one of two funding paths lost their largest participant. The right posture is the one the code already takes: no holder means no buyer, and the builder should report funding as unavailable rather than quote a fill that will not happen.
There is a second-order lesson we are carrying into every integration after this one. We score config risk on a lender - who can change what, behind what delay - and we had already flagged Terminal 1's own upgradeable diamond with an unidentified admin as an open item. The exploit landed one layer over, in a governance system whose formal safeguards existed and were bypassed by controlling the safeguards themselves. A cooldown a proposal can zero is not a cooldown. Neither is a timelock whose administrator is elected by a near-empty vote.
Takeaway
Term is two markets wearing one name. The auction is the price-discovery mechanism and the reason the protocol exists; the order book is what makes the market continuously usable, and it earns that by settling into the auction's own artifacts rather than competing with them. For anyone consuming this through an API, the practical effects are that a Term row's borrow gate is a window and not a constant, that its rate carries a 360-day convention it does not announce, and that both origination paths produce the same position underneath.
The parts that remain genuinely open are governance-shaped rather than code-shaped: the diamond is upgradeable, its admin is unidentified, and Terminal 1 has no published audit. After August, that is not an abstract concern.
Where to go next
- When lending leaves the pool - Midnight's peer-to-peer fixed-rate order book, the other half of this comparison.
- How we unify fixed-term lending - the two shapes of "fixed term" and how both map onto one model.
- The unified DeFi term sheet - how facts like "no early-repay rebate" reach a caller instead of a docs page.
- The 1delta API reference - the fixed-term fields, including the fill-now book, on every market we index.