← Documentation home
Curate

Loss realisation

How Morpho V2 picks up adapter losses automatically, where the mechanism can lag economic reality, and what the curator does about it.

There is no realizeLoss function. Earlier drafts of this page documented one — that was incorrect. Morpho V2 realises losses automatically inside accrueInterest() by summing every active adapter's realAssets() view. The curator's job isn't to call a missing button; it's to keep adapter realAssets() truthful and use the right lever when it isn't.

How loss realisation actually works

Every Morpho V2 adapter implements a view called realAssets() that returns its position's current on-chain value. MorphoMarketV1Adapter sums expectedSupplyAssets(marketId) across each Morpho Blue market it holds; the generic ERC-4626 adapter calls target.convertToAssets(target.balanceOf(this)); future adapters follow the same shape.

The vault's accrueInterest() function (in VaultV2.sol, the canonical Morpho V2 vault) loops over every active adapter, sums those views, and compares the new total to the stored _totalAssets. Any drop writes through to _totalAssets immediately, the share price drops, and the next previewRedeem(...) reflects the new value.

accrueInterest() runs at the head of every state-changing user-facing entry point — deposits, withdrawals, allocate/deallocate, fee accrual, role setters — so a loss surfaces on the very first interaction with the vault after the adapter's underlying market value drops. There is no separate curator-triggered step to "mark" it.

On the LayerCover wrapper, _syncAccounting() runs at the head of every meaningful entry point too (depositSenior, depositJunior, both request*, both withdraw*, syncAccounting), reads the wrapped vault's previewRedeem(totalVaultShares) at fresh prices, and pushes the delta through the senior/junior loss waterfall via _applyLoss(...). If the wrapped vault has accrued in this block, the wrapper sees the realised value; if not, calling vault.accrueInterest() first (or any state-changing entry) forces it.

Where the mechanism can lag

The "automatic" guarantee is only as good as each adapter's realAssets() view. Two staleness patterns to know:

1. The adapter's view depends on an underlying that hasn't accrued. A Morpho Blue market's expectedSupplyAssets doesn't recognise the interest accrued since the last touch on that market. If nobody has interacted with the market for several blocks, realAssets() underestimates a healthy market and overestimates one that's bleeding into bad debt. Touching morpho.accrueInterest(marketParams) on the underlying market forces it to fresh.

2. The adapter's view depends on an oracle or market the underlying doesn't yet reflect. A frozen oracle, an unliquidated bad-debt position, or an off-chain event (e.g. a real-world default the on-chain market hasn't priced yet) sits outside the chain's view. The adapter still reports its stale pre-event value. Until liquidations cascade through or the underlying takes a write-down, the LayerCover share price overstates the vault.

In both cases the symptom is identical: depositors can previewRedeem at an inflated price ahead of the true loss landing.

What the curator does about a known-but-not-yet-on-chain loss

LeverEffectWho can call
morpho.accrueInterest(marketParams) on the underlying Morpho Blue marketForces the market's own interest accrual. Picks up rate-driven drifts but doesn't conjure a write-down the market hasn't priced.Anyone (permissionless)
vault.accrueInterest() on the V2 vaultRe-sums every adapter's realAssets() and writes the delta into _totalAssets. Use this after touching the underlying market to push the new value through.Anyone (permissionless)
forceDeallocate(adapter, data, assets, onBehalf)Pulls assets back to idle at the adapter's current real value. The strongest tool — it crystallises the position out of the bad market, so any further deterioration only affects the smaller residual.Anyone (permissionless; pays the per-adapter penalty)
decreaseAbsoluteCap / decreaseRelativeCapLowers the affected adapter's cap so no further deposits route into it. Doesn't change current holdings.Curator OR sentinel (instant, no timelock)
setIsAllocator(addr, false)Locks out an allocator whose judgement is no longer trusted.Curator (timelocked)
removeAdapter(adapter)Retires the adapter entirely once its allocation is zero.Curator (timelocked)

The first two are read-the-truth tools: they don't change anyone's exposure, they just make the chain catch up to it.

The remaining four are containment: they limit damage to depositors who haven't realised the loss yet by either crystallising the position or refusing further allocations into the affected market.

What this means for senior LPs

The LayerCover wrapper's senior/junior waterfall (TranchedMetaVault._applyLoss) handles whatever the underlying vault tells it. If previewRedeem is honest, the waterfall is honest. If previewRedeem is overstating, the waterfall delays — junior absorbs less than it should, senior takes a stale-priced exit if it withdraws first.

The risk to senior LPs is therefore not a missed admin call. It's the staleness gap above. The defences against it are:

  1. The senior withdrawal notice itself. Senior exits go through a 5-day notice + 3-day execution window. That window exists so an honest curator can crystallise positions and push fresh realAssets() through the vault before any senior LP exits at a stale price.
  2. Curator vigilance. A diligent curator runs accrueInterest on affected markets + the vault when off-chain information indicates a loss is incoming, and forceDeallocates out of markets they've lost confidence in.
  3. The 20% junior buffer. Even with a stale price, junior absorbs the first 20% of any realised drawdown before any of it touches senior.

There is no "5-day SLA to call realizeLoss" — that was the wrong framing. The right framing is: the senior notice window is the slack the protocol gives a curator to keep adapter views honest before any senior LP can exit on a stale price.