Count the Price Sources That Can Write Your Balance Down
· 14 min read
- architecture
- defi
- evm
- risk
Count the Price Sources That Can Write Your Balance Down
Morpho's market documentation says risks are contained within each individual market. their own market documentation Morpho's own curator documentation says that on Vaults V1, where a bad price lets collateral be bought below oraclePrice times LLTV, that one market puts the entire vault at risk and supply caps do not protect against it. their own security considerations for curators Both pages were live the day I read them and both are correct. They are describing two different boundaries, and the money is standing on the second one.
The decision usually gets argued as isolation against pooling: separate markets contain a failure, one shared pool spreads it. Read either protocol's liquidation path and that framing stops describing what happens. What separates the designs is a count, and you can take it before you deposit.
Nothing here was observed on a deployed market: the numbers come from a harness in this run's own workspace, run against the projects' unmodified source, and they demonstrate where the accounting goes rather than what any depositor has lost. The source is Morpho Blue at commit d09dd1c4b9c7d9d05f976faa7ebfdc424dae5e8c and Aave as its own repository publishes it, both read rather than described, and the harness ships with this piece at proof/harness/_mb/test/ so every figure below can be run again rather than believed. No price source in this piece is evaluated for accuracy. That is a real question and it is not this one.
A price source you cannot repoint
In Morpho Blue a market's oracle address is one of the five fields hashed into its identity, so it is fixed at creation and pointing at a different oracle creates a different market with a different set of lenders. IMorpho.sol, struct MarketParams Create the market and the choice is over. There is no admin function to change it, because there is no admin path to the parameters at all.
Be precise about what that buys, because it is narrower than it sounds. It fixes the address. It does not fix what the contract at that address returns: an upgradeable oracle behind a fixed address changes the price rule without touching the market id. This piece audits no deployed oracle contract, so it stops at the address.
What the fixed address does buy is countability. A lender supplying into one market can name, before he commits anything, every price source the ACCOUNTING lets reach his balance, and there is one of them. Hold on to that number, and to the word accounting: the last section is about the path that goes round it.
Read where the shortfall lands, then read what your claim is priced off
Start with where the loss goes, because the two designs do not answer that the same way. When a liquidation ends with the borrower holding no collateral and debt still outstanding, the protocol subtracts the shortfall from that market's own supply total and from nothing else. Morpho.sol, the liquidation path One market, one number, and nothing in the contract reaches a second one.
The pooled design is not the mirror image the isolation story reaches for. Its shortfall is written per reserve rather than across the protocol, the half that story gets right. The half it misses is a loop. One liquidation of a borrower whose collateral is gone writes a deficit on every reserve he owed, not only on the one being repaid. LiquidationLogic.sol A lender who supplied one asset can be written down by a liquidation called on a different one, because the borrower happened to owe both.
It goes one turn further, and that turn decides the count. The health check reads a price for every reserve a borrower is in on either side, so a wrong price on something he merely borrowed is enough to start the liquidation. GenericLogic.sol The prices that can reach a lender are therefore not the collateral list on his reserve. They are every price any borrower in his reserve touches, on either side of that borrower's position.
So the boundary is a real difference, and it is still not what settles your exposure. The rest of this section is why.
Here is what does separate them. Take two markets on the unmodified source, lending the same asset, each supplied the same amount. Break one market's price source, let a borrower take a loan against collateral it is overvaluing, correct the price, and liquidate him down to nothing. Market A wrote off 306 of the 1,000 units it had been supplied, and market B's 1,000 did not move by one wei. The harness does not read that number back out of the run: the borrower owed 400, the collateral seized from him was worth 100 at the corrected price, the protocol's own incentive factor of 1.0638 turns that into 94 of debt repaid, and the shortfall is the 306 that is left.
That is the isolation working exactly as advertised. Now put a curated vault on top of it, which is the wrapper a depositor reaches when he wants a market picked for him. A MetaMorpho V1 vault's assets are the sum of its supply positions across every market in its withdrawal queue, so its share price carries whatever happens in any of them. MetaMorpho.sol Give that vault two holders with half the share register each, put both markets in the queue, and run the same liquidation. Market B never moved, and the holder still lost 153, because his share is priced off the sum of both markets and he owns half the register.
He never had a claim on market A, and could not have withdrawn from it, chosen it, or refused it. His claim was denominated in a sum that contained it, and that is the entire mechanism.
The obvious objection is the supply cap, and it is the one I would have raised: cap the bad market at zero and the vault cannot put anything into it. But the cap is not a property of the lending market at all; it lives in the vault above it, and it is consulted when the curator moves money and when the vault sizes its own deposits. MetaMorpho.sol, the cap sites Both of those are paths the vault controls. Nothing consults it on a path anybody else controls: a supply call made by one address and credited to another put 500 into market A on the vault's behalf, and the caller kept no claim on any of it. The protocol's supply function takes a beneficiary argument and checks only that it is not the zero address.
So the three totals, and the count on each.
WHERE THE SHORTFALL LANDS
---------------------------------------------------------------
Morpho Blue market[id].totalSupplyAssets
one market, one write, per liquidation
Aave debtReserve.deficit
one write per reserve the borrower owed,
all of them inside the same liquidation
WHICH TOTAL IS YOUR CLAIM PRICED OFF, AND HOW MANY PRICE
SOURCES CAN WRITE IT DOWN
---------------------------------------------------------------
direct supplier, that market's supply total
one Morpho market count: one you chose it, at deposit
supplier of one that reserve
Aave reserve count: NOT AN INTEGER YOU CAN READ.
every price any borrower in it touches,
on either side of his position, and the
set changes as borrowers come and go
governance and the
borrowers set it
holder of a curated the sum over the withdrawal queue
vault share count: one per market in the queue
the curator sets it,
and it changes without you
---------------------------------------------------------------Read down the second block and the finding is plain: the per market price source reduces the count to one, for exactly one participant, the lender who supplies a market directly and holds a claim on that market alone. Being that participant means evaluating a price source yourself and giving up whatever a curator earns by spreading across markets you did not pick. This piece measured nobody and will not tell you how many make that trade, only that the count differs across it.
The pooled design, at its strongest
The version worth arguing against is not a caricature. Aave keeps one oracle contract per pool instance, bound to that instance at construction and holding a source per asset that two admin roles can replace. AaveOracle.sol It is maintained by people paid to maintain it, and Aave's own changelog dates the completion of the v3.7 rollout to 2026-05-29, across ten deployments. Aave's changelog It is the current design and not a legacy one.
Three things about it are stronger than the isolation argument admits. Replaceable means repairable: a source that starts printing nonsense can be swapped, the one move a hashed address forecloses forever. Separate instances is a deliberate boundary, and the changelog names two of them on Ethereum alone. And a lender who supplies one reserve holds a claim denominated in that same reserve, so his two boundaries coincide with no curator involved, which is more than a vault share can say.
Where it loses is the count, and the count is the one number the design never puts in front of him. It is bounded and it is knowable, and knowing it means enumerating what every borrower in his reserve holds and owes rather than reading a parameter off a page. He did not choose it and he cannot see it from where he is standing.
The nearest formal work does not settle this. The nearest empirical study measured liquidations on Compound up to September 2020 and reported how promptly liquidable positions were taken, at the level of a position and a liquidator. Liquidations: DeFi on a Knife-edge I read its abstract rather than its body. Its unit is one borrower and one liquidator, its protocol is not either of these two, and its window closed years ago. The question here is one level up, at the pool, and it is a question that paper was not asking.
Somebody has to be paid to keep the count down
Choosing which failure a system may break on is the part that does not stop when the design is settled, because somebody carries it afterwards.
The system on my side of this is a settlement venue and not a lending market. It consumed a price I did not compute, which is the property it shares with everything above. What it does not share is the response. On a settlement venue the answer to a divergence is to halt, and halting there is safe, because nothing gets worse while you are stopped. In a lending market halting is the thing that creates the loss: a position that is underwater while liquidations are paused keeps sinking, and the shortfall is larger when you start again. The halt is the whole of what a divergence breaker does, and it is the move that does not survive the crossing.
What I have on my own breaker is two triggers, both correct, testnet, which is a sample of two and all of it on a test network. That tells me the band was not obviously wrong. It does not locate where it is wrong, and I would not carry it into a lending market as evidence of anything. The threshold was not a number somebody picked: it was a band tuned from testnet variance.
I have made that call, picked the single failure the system would be allowed to break on, while someone above me wanted a different one. What that call cost was not a line of code. On the venue I did build, the thing standing between a missed deadline and a stuck swap was a service monitoring claim/refund windows and firing refunds near timeout. A deadline and a price source are different objects that share what matters here: neither is watched by the contract, and both need somebody paid to watch them. That is the shape of what this article is recommending. Counting price sources does not remove the duty of watching them. It relocates it onto whoever owns the queue, for as long as the claim exists.
The ledger, then, with the parties named:
- the depositor reads a five field parameter set and forms a view on a price source, or hands it to a curator and takes the whole queue's count back in exchange
- the curator owns a standing duty to watch every price source in the queue, including markets he has capped at zero, which the protocol's own guidance tells him to remove from the queue outright, because a cap of zero leaves the donation path open while removal closes it
- the protocol team gives up repointing a market at a different price source, because that address is hashed into the identity. What is foreclosed is repointing rather than repair, and this is the row the recommendation hurts
- the borrower pays a worse rate than a pooled venue on the same collateral, because the lending against it is split across markets
- the liquidator shares an exit with every other market on that collateral, which is the next section
Where the count is a floor and not the count
Everything above counts price sources inside the accounting. A second path runs outside it, and it is where this argument was hardest to get right.
Three markets, one collateral asset, one venue to sell that collateral into. One market's price source is broken. The other two read the venue and are correct the entire time. Liquidating the broken market sells its seized collateral into the venue, which moves the price the other two markets are correctly reporting. Each of the three markets was supplied 1,000 units of the same loan asset, the same size as the two markets above. The market whose oracle stayed correct was written down by 153, and the market with the broken oracle by 140. That the first of those matches the vault holder's loss earlier is a coincidence; the two are unrelated.
The market with the correct price source lost more than the one with the wrong price. Nothing clever is happening: the first liquidation sells into full venue and the second into what it left, so the honest market's liquidator recovers less on collateral genuinely worth less by then. Its lenders were written down by a price source that the count said could not reach them.
The exit's depth is chosen for the experiment rather than taken off any venue, and it is the whole experiment. Deepening that exit a hundredfold and changing nothing else leaves the correct oracle market's lenders whole. So the effect is the exit, and that is knowable because removing the thinness removes the loss.
The condition is computable before you list anything. Sum your exposure to one collateral across every market holding it, then ask what a liquidator could sell at once on the venue he would use. The protocol caps the liquidator's incentive at fifteen percent over the debt he repays, and that cap binds only where the market's own liquidation loan to value is low. Morpho.sol, the liquidation incentive If your sum is larger than what clears inside that, the count you took is a floor rather than the count.
This section does not say how much depth is enough. There is no closed form, and the inputs are every position holding that collateral and a venue's depth at a moment nobody can pick in advance. A number here would be a model wearing a measurement's clothes. The two quantities are above; the comparison is yours.
What to take away
Name the accounting total your claim is denominated in, then count the distinct price sources that can write that total down.
The same rule decides a different question on a margin account: name the account your margin is denominated in, then count the mark prices that can write it down. Cross margin makes that count the whole position list, isolated margin makes it one, and a trader who buys cross margin for capital efficiency has bought the count as its price. It is the same trade the vault depositor makes for yield, one layer down.
Where this stops applying to you
Three conditions of your own system end this. Your depositors hold claims on single markets rather than on a vault, which almost none of them do. Every collateral you take is liquidated somewhere your own positions cannot move. Or you produce your own price. This piece read one protocol at a pinned commit, a second one at whatever its main branch held on the day, and two documentation pages on that same day. It opened no mainnet position and inspected no deployed vault.
Abdel KIARI
I’ve owned the EVM side of a DeFi protocol. I redesigned its architecture from scratch and built the Solidity infrastructure end to end.
Always happy to talk about interesting opportunities
abdel.kiari@gmail.com