Documentation

How Sheaf works

Sheaf lends dollars on Robinhood Chain (chain 4663) through Morpho Blue, and works out how to divide a deposit between markets so the whole of it earns as much as it can. It deploys no contract, takes no fee and holds no money.

1. A rate is a property of how full a market is

Every Morpho market on this chain uses the same rate model: the Adaptive Curve IRM at . It sets the borrow rate from one number, utilization u — the share of the market that has been lent out — and one stored level, rateAtTarget, which drifts slowly over days towards whatever keeps u near 90%.

Around that target the curve is a straight line, and it is four times as steep above 90% as below:

err  = (u − 0.9) / 0.1   if u > 0.9, else (u − 0.9) / 0.9
rate = rateAtTarget × (1 + err × 3)   if err > 0
rate = rateAtTarget × (1 + err × 0.75)  if err ≤ 0

A lender is paid out of what borrowers pay, in proportion to their share of the market: borrowed × rate × (1 − fee) ÷ supplied. Supplying raises the denominator and lowers u at the same time, which is why a deposit lowers its own rate twice over.

2. The division rule

At the best division of a deposit, every market used has been pushed to the same marginal rate — the next dollar would earn the same wherever it went. If one market's next dollar earned more than another's, moving a little money would pay for itself, and the division was not the best one.

That turns a search over every possible division into a search for a single number, the common marginal rate, which is a bisection. Two things about the problem are not smooth, so Sheaf handles them by enumeration instead: a market is either used or not used, and a placement may use at most four of them. With eight candidate markets that is 162 subsets, each water-filled and then evaluated exactly — cheap enough to redo on every keystroke.

The search runs in doubles; the answer does not. Whatever division the search lands on is evaluated once, exactly, in BigInt, through the same port of Morpho's arithmetic that everything else on the site uses. A search heuristic may be approximate. A number shown to somebody may not.

3. The port, and why it has to be exact

The chain can tell you what a market pays now. It cannot tell you what it would pay with your money in it — and that is the only number that matters when you are choosing where to put money. So js/irm.js is a line-by-line port of the deployed contracts' arithmetic: AdaptiveCurveIrm._borrowRate, ExpLib.wExp, MathLib.wTaylorCompounded, SharesMathLib, and Morpho's own _accrueInterest. It was written from the verified source of the contracts at these addresses, not from documentation.

tools/scan-markets.mjs checks it: for every market it asks the deployed rate model for borrowRateView at a pinned block, computes the same number locally, and requires them to be equal to the unit. On the last run that was 67 markets out of 67. "Close" would hide exactly the errors worth finding.

4. The transactions

Sheaf has no contract. A placement is one call to Bundler3 (), which runs a list of calls made by Morpho's own GeneralAdapter1 (). The adapter acts on the position of whoever sent the transaction and on no other address.

  • Deposit. You approve the adapter for exactly the deposit. The bundle pulls those dollars in and supplies each leg into its market, credited to your address. The legs sum to the deposit exactly, so nothing is left in the adapter.
  • Withdrawal from one market. Morpho's own withdraw, straight from your wallet, on your own position. No adapter and no permission.
  • Withdrawal from several. Morpho requires the adapter to be authorised to act for you. Rather than leave that authorisation standing, the transaction contains both ends of it: a signed grant, the withdrawals, and a signed revocation, over consecutive Morpho nonces. When the transaction ends, the permission it used no longer exists.

Every transaction is simulated with eth_call before your wallet is asked to sign it, so a revert costs nothing and can be explained in words.

5. Which markets Sheaf will use

A market is listed only if, at the block it was read:

  • it lends USDG — the only asset a Sheaf deposit is made of;
  • its rate model is the exact Adaptive Curve IRM above;
  • its market id re-derives from its own parameters;
  • its oracle answers, and agrees with a price from outside Morpho — Uniswap's own pools — to within 25%. A market's own oracle cannot corroborate itself;
  • it has at least $1,000 supplied.

Markets that fail any of those are shown nowhere and cannot be placed into, whatever is ticked. Beyond that the choice is yours: you tick the collateral you are willing to stand behind, and Sheaf only ever divides between markets that lend against it.

6. What can go wrong

  • A borrower goes under. Each market has a fixed liquidation threshold. If a collateral's price falls faster than liquidators act, the market can end up with bad debt, and lenders in that market carry it. Sheaf does not change this and cannot insure it.
  • The money is lent out when you want it. A withdrawal is paid from the cash a market has not lent. A market at 100% utilization cannot pay you until a borrower repays or somebody else supplies. The app shows what is free market by market, and the "keep it withdrawable" setting refuses to place into a market your deposit would leave above a chosen line.
  • An oracle is wrong. The Uniswap cross-check catches gross disagreement, not a subtle one, and not a feed that fails later.
  • The rate changes. It moves whenever anybody borrows or repays. Every rate here is what a market pays at the instant it was read.
  • This site goes away. Your position is a Morpho position owned by your address. It can be read and withdrawn from Morpho directly, and it does not depend on Sheaf existing.

7. How to check any of this

Every address the site uses is in one file, js/config.js, and linked to the explorer from the landing page. The market list is generated by a tool that pins one block and re-derives every id. The rate model is a port whose agreement with the deployed contract is asserted on every scan. The transaction builder is a pure module loaded by both the page and the test suite, so what is tested on a fork is what your wallet is asked to sign.