How it works

The whole mechanism

Curb is a front end for two pieces of Morpho's infrastructure that were already deployed on Robinhood Chain before this site existed: Vault V2 and Market V1. Nothing here is ours. This page says exactly what happens.

The pieces

Morpho Blue — "Market V1"
An immutable lending market. Each one pairs one loan asset with one collateral asset, one oracle, one rate model and one liquidation threshold, fixed forever at creation. On this chain they lend USDG against tokenized stocks.
Vault V2
An ERC-4626 vault that lends its deposits into markets through adapters, under caps its curator sets. This is what Curb raises for you.
MorphoMarketV1AdapterV2
The one contract allowed to move your vault's dollars into Morpho Blue. One per vault, made by its own factory. It refuses any market whose rate model is not the Adaptive Curve IRM.
Uniswap SwapRouter02
Used for one thing: turning ETH into the dollars a vault takes.

The three transactions

  1. VaultV2Factory.createVaultV2(you, USDG, salt) — deploys the vault. You are its owner from that block. About 4.5m gas.
  2. createMorphoMarketV1AdapterV2(vault) — deploys the adapter, which approves the vault and Morpho to move its dollars. About 2.5m gas.
  3. vault.multicall([...]) — one transaction that makes you curator and allocator, adds the adapter, sets a curb on every market you chose, sets the rate ceiling, and points deposits at your first market. About 1m gas.

The third one is possible because multicall on a Vault V2 delegatecalls into itself: msg.sender stays you for every call in the list. Each curator action is also timelocked, and a fresh vault's timelocks are all zero — so each change is submitted and executed in the same transaction. That window closes the moment you set a timelock, which is a promise to your depositors that the shape of the book cannot change without notice.

Curbs

A Vault V2 caps exposure by bytes32 id, and the adapter decides what its ids are. For every market it returns three:

keccak(abi.encode("this", adapter))                      everything through this adapter
keccak(abi.encode("collateralToken", collateral))        everything backed by this stock
keccak(abi.encode("this/marketParams", adapter, params)) this one market

So a curb is really three caps, and a market's room is the smallest of them. That is what stops a second AAPL market from quietly borrowing the first one's headroom. Curb sets all three when you raise a vault, and moves the shared two by the difference when you change one later — setting them to the new per-market figure would try to lower a shared cap, which the contract refuses outright.

Relative caps are set to 100%. A relative cap is measured against the vault's total assets before the deposit that is being checked, so anything lower makes deposits revert while a vault is still small.

The rate ceiling, which is not optional

A Vault V2 is created with maxRate = 0. Total assets are min(realAssets, lastAssets · maxRate · elapsed), so at zero the share price cannot move: the markets earn and the depositors do not. Curb sets the contract's own maximum, 200% a year — a guard against a donation spike, not a target. The test suite keeps a second vault built the default way to prove the difference is real, and the app marks any vault it finds in that state pays nothing.

Getting out

A withdrawal is served from the vault's idle dollars and then from one market — whichever the allocator pointed the liquidity adapter at. If the money has been moved elsewhere, a plain redemption reverts inside the adapter with an arithmetic panic and no message.

It is not stuck. Vault V2 gives every holder an in-kind redemption right: forceDeallocate pulls a named market back into the vault, and anyone may call it on their own behalf. Curb plans that route for you — it works out which markets to pull and how much, and sends the pulls and the withdrawal in one transaction through the vault's own multicall. It costs whatever penalty the curator set, which is zero on a vault raised here.

Two details the planner exists for. Every margin points one way: a market pays out its whole reported position, but by the time a transaction lands the amount owed has grown — on the whole book, while any one market grows only on its own. So the plan leaves the liquidity market a buffer, and where it cannot, it withdraws the exact dollars it can prove will be there rather than reverting over the last few units.

Where the risk is

  • The collateral. A vault that lends against a thin stock at a high liquidation threshold loses money when that stock gaps and liquidators cannot clear the position. That is the curator's decision; Curb shows depth, oracle and threshold beside every curb.
  • The oracle. Each market names its own. Curb refuses to list a market whose oracle disagrees with the stock's Uniswap price by more than 25%, and says so when it cannot check at all.
  • Liquidity. Interest comes from borrowers, so a fully-lent market has nothing free to pay withdrawals with until someone repays.
  • The curator. A curator can raise curbs, add markets and take a performance fee. With no timelock, immediately. Read the vault before depositing — the app shows every one of those settings.
  • Curb itself is a website. If it disappears, your vault does not: it is a contract you own, and Morpho's own interface and any Ethereum tool can reach it.

What was checked, and how

The suite runs against a fresh fork of the live chain, through the same builder this site loads — js/vault.js — so the transaction a test sends is the transaction the page sends. Two of its checks exist to catch the framework lying to itself: a vault built the default way that must pay nothing, and a plain redemption that must fail before the planned one is allowed to succeed. It found four real bugs in this app before you saw it.