How Morpho Vault V2 Works - Adapters, Caps, Timelocks and In-Kind Redemption
Morpho is a lending protocol on Ethereum and other EVM chains. Its base layer, Morpho Blue, is a set of isolated and immutable lending markets; its vaults sit on top and let a depositor lend to several markets at once without choosing them. Vault V2 is the second generation of those vaults, and its main change is that a vault no longer talks to Morpho Blue directly but through pluggable contracts called adapters.
This article explains how a Vault V2 is built: the contracts involved, the four roles and what each one may do, the timelock system that protects depositors from configuration changes, the cap system expressed over abstract identifiers, the share accounting with its rate limit and fees, and the exit paths, including the in-kind redemption performed with forceDeallocate. Every statement is taken from the morpho-org/vault-v2 source at the commit pinned in the references. A companion article, Monitoring a Morpho Vault V2 - What a Depositor Needs to Watch, starts from the same code and looks at it from the depositor’s side: where losses come from and which on-chain signals reveal them.
This article has been made with the help of Claude Code and several custom skills
From MetaMorpho to Vault V2
The first generation of Morpho vaults, MetaMorpho (now called Morpho Vaults V1), supplies a single asset to a list of Morpho Blue markets, ordered by a supply queue and a withdraw queue, with one cap per market. Its design is tied to Blue: a vault can only hold Blue supply positions. How Morpho Vault V1 (MetaMorpho) Works, and What Vault V2 Changes describes it in detail.
Vault V2 keeps the idea of a curated, non-custodial ERC-4626 vault and generalises the rest:
| Concern | Vaults V1 (MetaMorpho) | Vault V2 |
|---|---|---|
| Where funds go | Morpho Blue markets only | Any protocol for which an adapter exists |
| Risk limits | One supply cap per market | Absolute and relative caps per abstract id (market, collateral, adapter) |
| Exit when markets are illiquid | Wait for liquidity | In-kind redemption with forceDeallocate |
| Configuration changes | Timelock for sensitive actions | Per-function timelock, plus permanent abdication |
| Access control | None on deposits and transfers | Four optional gate contracts |
| Interest smoothing | None | maxRate cap on share-price growth |
The core contract is VaultV2.sol, about 900 lines. It is immutable: there is no proxy and no upgrade path. New vaults are deployed by VaultV2Factory.
Architecture
A Vault V2 holds two kinds of assets: an idle balance of the underlying token, kept in the vault contract itself, and allocated assets, held by adapters on the vault’s behalf.
flowchart TB
U["Depositor<br/>holds vault shares"] -->|"deposit / withdraw"| V["VaultV2<br/>shares, totalAssets, caps, fees"]
V --> I["Idle assets<br/>token balance of the vault"]
V -->|"allocate / deallocate"| A1["MorphoMarketV1AdapterV2"]
V -->|"allocate / deallocate"| A2["MorphoVaultV1Adapter"]
A1 --> M1["Morpho Blue market 1"]
A1 --> M2["Morpho Blue market 2"]
A2 --> MM["MetaMorpho vault (V1)"]
R["Adapter registry<br/>(optional)"] -.->|"isInRegistry"| V
G["Gates<br/>(optional)"] -.->|"canSend / canReceive"| V
The repository contains the following contracts at the pinned commit:
VaultV2andVaultV2Factory: the vault and its deployer.- Adapters:
MorphoMarketV1AdapterV2supplies to Morpho Blue markets that use the AdaptiveCurveIRM interest-rate model;MorphoVaultV1Adapterdeposits into a MetaMorpho vault;MidnightAdaptertargets Morpho Midnight, the fixed-rate protocol, and is still described as work in progress in the README. Each adapter has its own factory. - Registries:
MorphoMarketV1RegistryV2andMorphoVaultV1Registryaccept only adapters deployed by the official factories, andRegistryListis an append-only list of such sub-registries. - Gates:
WhitelistReceiveSharesGateandWhitelistSendAssetsGate, two reference implementations. BluePublicAllocator: a periphery contract that lets anyone, not only the vault’s allocators, move liquidity between Blue markets within limits set by the allocators. The caller pays a penalty proportional to the assets moved, which goes to the vault.
The adapter interface has three functions:
interface IAdapter {
function allocate(bytes memory data, uint256 assets, bytes4 selector, address sender)
external returns (bytes32[] memory ids, int256 change);
function deallocate(bytes memory data, uint256 assets, bytes4 selector, address sender)
external returns (bytes32[] memory ids, int256 change);
function realAssets() external view returns (uint256 assets);
}
allocate and deallocate move funds in and out of the target protocol and return two things: the list of ids the position belongs to, and the change in the value of the position. realAssets returns the current value of everything the adapter holds. The vault never inspects the target protocol; it trusts the adapter’s report. This is why the choice of adapters is a curator decision, behind a timelock, and why a registry can restrict it further. The rules an adapter must follow, the three adapters in the repository and the Vault V1 adapter’s caveats are covered in Morpho Vault V2 Adapters - How a Vault Allocates, and Why It Wraps a Vault V1.
Roles
Four roles act on a vault. Only one address can be owner and one curator; there can be several allocators and sentinels.
| Role | Set by | Can do | Cannot do |
|---|---|---|---|
| Owner | Previous owner | Set the curator and sentinels, set name and symbol | Move funds or change risk parameters |
| Curator | Owner | Submit configuration changes: adapters, registry, caps, gates, allocators, fees, timelocks, forceDeallocate penalties. Decrease caps immediately. Revoke pending changes |
Execute an increase in risk before its timelock has passed |
| Allocator | Curator (timelocked) | allocate and deallocate within the caps, set the liquidity adapter, set maxRate |
Change caps, adapters or fees |
| Sentinel | Owner | deallocate, decrease caps, revoke pending changes |
Allocate, or add risk in any form |
The comments in the source summarise the intent. The owner cannot directly hurt depositors, the curator cannot do so without going through a timelock, and allocators move funds only inside the bounds the curator set. Roles are not two-step: anyone can be given a role without accepting it.
Two allocator powers take effect without any timelock. Setting the liquidity adapter can make deposits or withdrawals revert, and setting maxRate to zero stops the share price from rising. Neither can block an in-kind redemption, described further below.
Timelocks and abdication
Every curator function except the two cap decreases is protected by timelocked(). A change goes through two steps:
- The curator calls
submit(data), wheredatais the full calldata of the future call. The vault recordsexecutableAt[data] = block.timestamp + timelock[selector]. - Once that time has passed, anyone can send the exact same calldata.
timelocked()checks that the data was submitted, that the delay has passed and that the function was not abdicated, then clears the entry.
Between the two, the curator or any sentinel can cancel the change with revoke(data).
sequenceDiagram
autonumber
participant C as Curator
participant V as VaultV2
participant S as Sentinel
participant X as Anyone
C->>V: submit(abi.encodeCall(increaseAbsoluteCap, (idData, cap)))
V-->>V: executableAt[data] = now + timelock[selector]
Note over V: Depositors who disagree can exit during the delay
alt Sentinel objects
S->>V: revoke(data)
V-->>V: executableAt[data] = 0
else Delay elapsed
X->>V: increaseAbsoluteCap(idData, cap)
V-->>V: timelocked() checks and clears the entry
end
Timelocks are set per function selector, and three details matter:
- Decreasing a timelock is itself timelocked by the timelock being decreased.
submitreads the selector inside thedecreaseTimelockcalldata and appliestimelock[thatSelector]. A curator who set seven days onaddAdapterneeds seven days to lower it. - Increasing a timelock is immediate if the timelock of
increaseTimelockis zero, which is the default. The source warns that a very large value can make a function impossible to submit for good, sinceblock.timestamp + timelockmust not overflow. abdicate(selector)disables a function permanently. Data can still be submitted for it, buttimelocked()will revert. A curator who abdicatessetAdapterRegistryafter setting an append-only registry commits the vault to that family of adapters forever.
At deployment every timelock is zero and no gate is set, so the curator can configure the vault quickly. The source recommends batching the gate configuration with the creation if the vault must not be open, even briefly. Depositors should check the timelocks on the deployed vault rather than assume them.
The cap decreases, decreaseAbsoluteCap and decreaseRelativeCap, are not timelocked and can be called by the curator or a sentinel. Lowering a cap only reduces risk, so there is no reason to delay it.
Ids and caps
Vault V2 does not cap markets directly. It caps ids: a bytes32 that names a risk factor several positions can share. On every allocate and deallocate, the adapter returns the ids of the position, and the vault adds the change to the allocation of each of them.
MorphoMarketV1AdapterV2 returns three ids for each Blue market:
ids_[0] = adapterId; // keccak256("this", adapter)
ids_[1] = keccak256(abi.encode("collateralToken", marketParams.collateralToken));
ids_[2] = keccak256(abi.encode("this/marketParams", address(this), marketParams));
The second id is the useful one: two markets with the same collateral and different oracles or liquidation thresholds both count against the same collateral cap. A curator can therefore limit exposure to, for example, one liquid staking token across every market that accepts it, whatever adapter or market the funds sit in. MorphoVaultV1Adapter returns only its own adapter id, since a MetaMorpho vault is treated as a single position.
Each id carries two limits:
- Absolute cap: a maximum allocation in units of the underlying asset. An absolute cap of zero blocks allocation entirely, which is how a market or adapter is switched off.
- Relative cap: a maximum fraction of the vault’s total assets, in WAD (
1e18= 100 %). A value of exactly1e18disables the check.
The checks run in allocateInternal, after the adapter has reported its change:
where \(A^{\text{first}}\) is firstTotalAssets, the vault’s total assets after the first interest accrual of the current transaction. It is stored in transient storage (EIP-1153), so it resets at the end of each transaction. Measuring the relative cap against this value rather than the live total stops an allocator from inflating total assets with a flash-loaned deposit, allocating against the larger base and withdrawing again within the same transaction.
Three consequences follow from where the checks sit:
- Caps are checked on allocation only. Interest earned in a market, or a donation to an adapter, can push an allocation above its cap without any revert.
- Relative caps are soft. Withdrawals reduce total assets without touching allocations, so the fraction held in a market can exceed its relative cap after other depositors leave.
- Allocations can be stale. An id’s
allocationis updated only when someone allocates to or deallocates from that position, so it does not reflect interest or losses accrued since then. The source advises tracking allocations from theAllocateandDeallocateevents.
Accounting
Total assets and the max rate
The vault stores _totalAssets, the last recorded value of its holdings. Interest and losses are accounted once per transaction, at the first interaction that calls accrueInterest(). That function computes the real value of the vault, the idle balance plus the realAssets() of every adapter, and then applies the rate limit:
Here \(A^{0}\) is the stored total, \(\Delta t\) the time since the last update, and \(r\) the allocator-set maxRate, a per-second rate in WAD capped at 200 % per year. \(A^{1}\) becomes the new _totalAssets and \(I\) is the interest distributed.
The minimum has an asymmetric effect:
- Gains are smoothed. If the markets earned more than
maxRateallows, the excess stays in the vault, unaccounted, and is distributed in later periods. A curator can use this to keep the displayed rate stable or to build a buffer against future losses. - Losses are not. If \(R\) is lower than \(A^{0}\), the new total is \(R\) and the share price drops at once.
Donations also count as gains. Tokens sent to the vault, and the penalties paid on forceDeallocate, raise \(R\) and are distributed at most at maxRate. The source notes that a high displayed rate can attract opportunistic depositors who dilute the interest, which a lower maxRate mitigates.
Accounting once per transaction is also a security measure. Because later interactions in the same transaction see the stored total and not a recomputed one, an attacker cannot flash-borrow shares, trigger a loss and profit from the price movement within one transaction. Lending shares over longer periods remains a risk, and the source states that vault shares should not be loanable.
Fees
Two fees are paid to recipients set by the curator, in newly minted shares:
\[\begin{aligned} F^{p} &= I \cdot f^{p}, \qquad f^{p} \le 50\,\% \\ F^{m} &= A^{1} \cdot \Delta t \cdot f^{m}, \qquad f^{m} \le 5\,\% \text{ per year} \end{aligned}\]The performance fee \(F^{p}\) is a share of the distributed interest, so it follows maxRate and not the real interest. The management fee \(F^{m}\) is a share of total assets and is taken whether the vault gains or loses, so it can lower the share price on its own. Both are converted to shares against the total net of fees, and both are rounded down. A fee is only taken if its recipient passes the receive-shares gate.
totalSupply does not include the fee shares that would be minted at the next accrual. The preview* and convertTo* functions do include them, by calling accrueInterestView().
Share conversion
The vault uses one virtual asset and \(v = 10^{\max(0, 18 - d)}\) virtual shares, where \(d\) is the decimals of the underlying token. Vault shares therefore have 18 decimals for any asset with 18 decimals or fewer. With \(S\) the share supply including pending fee shares:
\[\begin{aligned} \text{shares on deposit} &= \left\lfloor a \cdot \frac{S + v}{A^{1} + 1} \right\rfloor \\ \text{assets on redeem} &= \left\lfloor s \cdot \frac{A^{1} + 1}{S + v} \right\rfloor \end{aligned}\]mint and withdraw round up instead, so every conversion favours the vault. The virtual shares make the classic ERC-4626 inflation attack expensive, but the source still recommends seeding a new vault with an initial deposit, both against that attack and because repeated dust losses from adapter rounding could otherwise move the share price noticeably in a near-empty vault.
Deposits and withdrawals
A deposit or a withdrawal goes through the following steps. The vault checks the gates, moves the tokens and updates _totalAssets, then uses the liquidity adapter if the allocator has set one.
flowchart TB
subgraph Deposit
D1["accrueInterest()"] --> D2["Check gates:<br/>receiver may receive shares,<br/>sender may send assets"]
D2 --> D3["Pull tokens, mint shares,<br/>_totalAssets += assets"]
D3 --> D4{"Liquidity adapter set?"}
D4 -->|"yes"| D5["allocate(liquidityAdapter, assets)<br/>caps checked, may revert"]
D4 -->|"no"| D6["Assets stay idle"]
end
subgraph Withdraw
W1["accrueInterest()"] --> W2["Check gates:<br/>owner may send shares,<br/>receiver may receive assets"]
W2 --> W3{"Idle balance enough?"}
W3 -->|"no, adapter set"| W4["deallocate(liquidityAdapter,<br/>assets - idle)"]
W3 -->|"yes"| W5["Burn shares, transfer assets"]
W4 --> W5
end
The liquidity adapter and its data are a single pair, used for both entry and exit, so that depositing then withdrawing the same amount leaves the allocation unchanged. A typical choice is a liquid Morpho Blue market. Every other allocation is the allocators’ job, done with explicit allocate and deallocate calls.
Two behaviours differ from what an ERC-4626 integrator may expect:
- The
max*functions always return zero.maxDeposit,maxMint,maxWithdrawandmaxRedeemreturn 0 because the vault cannot guarantee that a gate call or an adapter call will not revert. An integration that reads them as limits will conclude that the vault accepts nothing. The Morpho documentation describes how to compute actual limits from the caps and the market liquidity. - A deposit can revert on a cap. If the liquidity adapter’s ids are at their absolute or relative cap,
depositandmintrevert. On an empty or nearly empty vault the relative cap check is likely to fail, which is another reason to seed the vault.
The vault also implements ERC-2612 permit on its shares, with an EIP-712 domain made of the chain id and the vault address, and a multicall that lets an externally owned account batch several administrative calls.
In-kind redemption with forceDeallocate
A withdrawal can only pay from idle assets and the liquidity adapter. If both are empty and the allocators do not move funds, a depositor cannot exit through withdraw. Vault V2 adds a second exit, forceDeallocate, which anyone can call:
function forceDeallocate(address adapter, bytes memory data, uint256 assets, address onBehalf)
external returns (uint256)
{
bytes32[] memory ids = deallocateInternal(adapter, data, assets);
uint256 penaltyAssets = assets.mulDivUp(forceDeallocatePenalty[adapter], WAD);
uint256 penaltyShares = withdraw(penaltyAssets, address(this), onBehalf);
emit EventsLib.ForceDeallocate(msg.sender, adapter, assets, onBehalf, ids, penaltyAssets);
return penaltyShares;
}
It moves assets from an adapter back to the vault’s idle balance, then charges onBehalf a penalty, set by the curator per adapter and capped at 2 %. The penalty is charged as a withdrawal whose receiver is the vault itself: the caller’s shares are burned and the assets stay in the vault, where they count as a donation to the remaining depositors at the next accrual.
On its own, forceDeallocate only frees liquidity. Combined with a flash loan it becomes an in-kind redemption: the depositor leaves the vault and ends up holding a position in the underlying market instead of the vault’s token.
sequenceDiagram
autonumber
participant U as Depositor
participant F as Flash lender
participant M as Blue market
participant V as VaultV2
U->>F: flash-borrow X of the asset
U->>M: supply X on own account
U->>V: forceDeallocate(adapter, market, X, onBehalf = U)
V->>M: adapter withdraws X (the liquidity U just supplied)
V-->>V: penalty: burn U's shares worth X times penalty
U->>V: withdraw(X) from the now idle balance
U->>F: repay X
Note over U,M: U now holds X of supply in the market, not in the vault
The source gives the optimal amount for a depositor holding \(a\) assets in a fully illiquid vault: \(\min\left(L, a / (1 + p)\right)\), where \(L\) is the market’s available liquidity and \(p\) the penalty.
The penalty exists because relative caps are not checked on exit. Without it, anyone could reshape the vault’s allocation for free by forcing deallocations from chosen markets. With a 0 % penalty, the only cost is gas.
In-kind redemption and timelocks together form the vault’s non-custodial guarantee: when the curator submits a change that adds risk, every depositor can leave before it takes effect, even if no market has liquidity, provided the relevant timelocks are long enough to act on.
Gates
A vault can be restricted by up to four gate contracts. Each is a single view function returning a boolean, and a gate set to the zero address imposes no restriction.
| Gate | Checked on | Effect if it refuses or reverts |
|---|---|---|
receiveSharesGate |
Deposit and mint (receiver), transfer (receiver), fee minting | Can block all vault interactions if it reverts or runs out of gas; can prevent a user from getting shares back from another contract |
sendSharesGate |
Withdraw and redeem (owner), transfer (sender) | Can lock users inside the vault |
receiveAssetsGate |
Withdraw and redeem (receiver) | Can lock users inside the vault; the vault itself is always allowed, so penalties still work |
sendAssetsGate |
Deposit and mint (sender) | Restricts entry only; cannot block existing funds |
Gates turn a Vault V2 into a permissioned vault, for example one open only to KYC-verified addresses. The source documents the limit of the approach: a whitelisted address can hold shares on behalf of others, so a share gate controls who holds the token, not who benefits from it. Three of the four gates can trap funds, which is why setting them is a timelocked curator action.
Losses
A loss enters the vault’s accounting through realAssets(). When a Blue market writes off bad debt, the value of the adapter’s supply shares drops, the next accrueInterest() sees a total below the stored one, and the share price falls for every holder at once. Losses are spread across holders in proportion to their shares, with no priority between depositors.
Two mechanisms on the adapter side are relevant:
burnShares(marketId)onMorphoMarketV1AdapterV2is a timelocked curator action that writes a market position to zero in the adapter’s books, so the loss is realised at the next accrual. The source notes that it works even on a market whose interest-rate model reverts, a case that would otherwise makerealAssets()revert. Because it is timelocked, depositors watching the vault can exit before it executes, and the source recommends setting the market’s caps to zero when submitting it.- Liveness depends on adapters. If any adapter’s
realAssets()reverts, for example because one of its markets can no longer compute interest,accrueInterest()reverts, and with it every deposit, withdrawal and allocation.
What a depositor can do about these risks, and which events reveal them early, is the subject of the companion monitoring article.
Conclusion
Morpho Vault V2 is an immutable ERC-4626 vault that delegates every interaction with a lending protocol to adapters, and constrains what its operators can do through caps, timelocks and gates.
- Adapters and ids separate the vault from the protocols it uses. A cap applies to an abstract id, such as a collateral token, and therefore across all markets that share it.
- Caps are enforced on allocation only. Interest, donations and withdrawals can move allocations above their limits without a revert.
- Roles are graduated. The owner appoints, the curator configures behind per-function timelocks, allocators move funds within the caps, and sentinels can only reduce risk.
- Timelocks are per selector, decreasing one costs its own duration, and
abdicatemakes a function unusable forever. - Accounting happens once per transaction. Gains are distributed at most at
maxRate, losses are recognised immediately, and the management fee applies in both cases. - Two exit paths exist.
withdrawuses idle assets and the liquidity adapter;forceDeallocate, combined with a flash loan, lets a depositor exit into the underlying market for a penalty of at most 2 %. - The main risks are the credit and oracle risk of the held markets, an immutable core that cannot be patched, and a configuration only as protected as its timelocks: on the vault read on 22 October 2026, adapters and caps needed three days of notice, while fees and the force-deallocation penalty could change at once under a one-signer curator Safe.

Annex
Key Terms
| Term | Definition |
|---|---|
| ERC-4626 | The Ethereum standard for tokenized vaults, defining deposit, mint, withdraw and redeem against a single underlying asset and the conversions between assets and shares. |
| Share price | The amount of underlying asset one vault share can be redeemed for, equal to total assets divided by total shares, with virtual amounts added. |
| Morpho Blue | Morpho’s base lending layer, also called Morpho Market V1, made of isolated immutable markets each defined by a loan token, a collateral token, an oracle, an interest-rate model and a liquidation threshold. |
| MetaMorpho | The first generation of Morpho vaults (Vaults V1), which supply directly to Morpho Blue markets through supply and withdraw queues. |
| Adapter | A contract that holds positions in an external protocol on the vault’s behalf, reports their value through realAssets(), and returns the ids and value change on each allocation. |
| Id | A bytes32 naming a risk factor shared by several positions, such as an adapter, a collateral token or a single market; caps and allocations are tracked per id. |
| Allocation | The amount of assets recorded against an id, updated only when the vault allocates to or deallocates from a position carrying that id. |
| Absolute cap | The maximum allocation of an id in units of the underlying asset; zero forbids any allocation. |
| Relative cap | The maximum allocation of an id as a fraction of firstTotalAssets, in WAD, where 1e18 disables the check. |
| firstTotalAssets | The vault’s total assets after the first interest accrual of a transaction, kept in transient storage and used as the base for relative caps. |
| Idle assets | The balance of the underlying token held by the vault contract itself, used first to pay withdrawals. |
| Liquidity adapter | The adapter and data pair, set by allocators, to which deposits are forwarded and from which withdrawals beyond the idle balance are taken. |
| Curator | The single role that configures the vault (adapters, caps, gates, fees, allocators, timelocks) through timelocked submissions. |
| Allocator | A role that moves funds between adapters within the caps and sets the liquidity adapter and maxRate, without timelock. |
| Sentinel | A role that can only reduce risk: deallocate, decrease caps and revoke pending curator changes. |
| Timelock | The per-function delay between a curator’s submit and the moment anyone can execute the submitted call. |
| Abdication | A curator action that permanently disables a timelocked function. |
| maxRate | The maximum per-second growth rate of total assets, set by allocators and capped at 200 % per year; real gains above it remain undistributed. |
| forceDeallocate | A permissionless function that moves assets from an adapter to the idle balance and charges the caller’s chosen account a penalty of up to 2 %. |
| In-kind redemption | An exit in which the depositor, using a flash loan and forceDeallocate, leaves the vault holding a position in an underlying market. |
| Gate | An optional external contract that approves or refuses receiving shares, sending shares, receiving assets or sending assets. |
Invariants
| Invariant | Enforced by | Breaks if |
|---|---|---|
| No allocation increases an id above its absolute cap, and no allocation goes to an id whose absolute cap is zero. | The checks in allocateInternal after each adapter call. |
An adapter returns incomplete ids or under-reports the change. |
An allocation is limited to firstTotalAssets times the id’s relative cap at the time of allocation. |
allocateInternal and the transient firstTotalAssets. |
Allocators bring their own short-term capital to raise total assets; the source notes this costs capital but is possible. |
| Interest and losses are accounted at most once per transaction. | accrueInterestView returning the stored values once firstTotalAssets is set. |
Transient storage semantics change, or a code path reads realAssets directly. |
Distributed total assets never grow faster than maxRate. |
The min(realAssets, maxTotalAssets) in accrueInterestView. |
maxRate is raised, which allocators can do without timelock up to the 200 % ceiling. |
| A curator change that adds risk takes effect no earlier than its timelock. | submit and timelocked(), with decreaseTimelock bound to the target selector’s timelock. |
The relevant timelock is zero, which is the default at deployment. |
| A non-zero fee always has a non-zero recipient. | The FeeInvariantBroken checks in the four fee setters. |
None at this commit; all four setters check it. |
A depositor can exit even when no market is liquid enough for withdraw. |
forceDeallocate callable by anyone, penalty capped at 2 %. |
An adapter reverts on deallocate while its market is liquid, or a gate refuses the depositor. |
Integration Notes
| Behaviour | What an integrator should do |
|---|---|
maxDeposit, maxMint, maxWithdraw and maxRedeem always return 0. |
Do not use them as limits. Compute limits from caps, gate status and the liquidity adapter’s available liquidity, as described in the Morpho documentation. |
totalSupply excludes fee shares not yet minted. |
Use previewRedeem or convertToAssets for share values, or call accrueInterestView to get the up-to-date supply. |
deposit can revert when the liquidity adapter’s ids are at their caps, especially on an empty vault. |
Seed new vaults with an initial deposit and handle AbsoluteCapExceeded and RelativeCapExceeded reverts. |
An id’s allocation does not include interest or losses since the last allocation to it. |
Read positions from the adapter (for example expectedSupplyAssets) or track Allocate and Deallocate events. |
| A loss reduces the share price immediately at the next interaction, and the management fee is taken even during losses. | Do not assume the share price is monotonic; protect against price drops with explicit checks around deposits and redemptions. |
| A gate that reverts can block every interaction, including fee minting. | Check gate addresses and their behaviour before integrating, and treat a change of gate as a significant event. |
| Vault shares should not be loanable. | Do not list Vault V2 shares on lending markets where they can be borrowed and shorted ahead of a known loss. |
Risks
Morpho’s own risk disclosure for Vault V2 is the repository README, the warnings in the source, and the audit reports stored next to the code. The README states that all contracts are immutable, that relative caps can be exceeded after withdrawals, that the max* functions return zero, and that the non-custodial guarantee holds only “if the right timelocks are not zero”. This section adds what the code and one deployed vault show, and stays at the level of a summary: which on-chain signals reveal each risk, and a watcher built on them, are the subject of Monitoring a Morpho Vault V2 - What a Depositor Needs to Watch.
The configuration figures below come from Sentora RLUSD Main (0x6dC58a0FdfC8D694e571DC59B9A52EEEa780E6bf), the vault the monitoring article observed, read on Ethereum mainnet on 22 October 2026 at block 26,248,478. They describe one vault at one block. Every Vault V2 has its own roles, timelocks and adapters, and must be read on its own.
Smart contracts and transparency
- The core cannot be upgraded, in either direction.
VaultV2has no proxy (see From MetaMorpho to Vault V2). The observed vault is verified on Etherscan asVaultV2(Solidity 0.8.28), was created on 4 March 2026 at block 24,587,448 by a contract verified asVaultV2Factory(0xA1D94F746dEfa1928926b84fB2596c06926C0405), and that factory’sisVaultV2returns true for it. A bug found in the vault cannot be patched: the remedy is for depositors to leave and for the curator to open a new vault. In return, nobody can replace the logic with a malicious or faulty version, and the ceilings described above (50 % performance fee, 5 % a year management fee, 2 % force-deallocation penalty, 200 % a yearmaxRate) and the timelock mechanics cannot change under a depositor. - The configuration is what moves. Which adapters hold the funds, how much each id may receive, the fees, the gates and the timelocks are state written by the roles. For a depositor, the upgrade question of an upgradeable protocol becomes a governance question here, covered under Centralisation and governance below (derived).
- Audits and formal verification. The repository’s
auditsfolder holds reports by Spearbit (several rounds), Zellic, a public competition, Blackthorn and ChainSecurity for the vault, and Blackthorn, Certora and Spearbit for the market adapter; acertorafolder holds formal-verification specifications. This article did not compare the deployed bytecode with the pinned commit, which is later than the vault’s creation (see Gaps). - Accounting that integrators misread. The
max*functions returning zero,totalSupplywithout pending fee shares, stale allocations and caps checked only on allocation are listed in the Integration Notes. They are traps for code built on top of the vault rather than flaws in its own accounting.
External dependencies
- Adapters are trusted for value. The vault takes each adapter’s
realAssets()at face value (Architecture), so a faulty adapter misprices every share, and an adapter whoserealAssets()reverts stops every deposit, withdrawal, allocation andforceDeallocate, since the latter ends with awithdrawthat accrues first (Losses). On the observed vault,setAdapterRegistryis abdicated with a registry verified asRegistryList(0x3696c5eAe4a7Ffd04Ea163564571E9CD8Ed9364e), whose four sub-registries are verified asMorphoMarketV1Registry,MorphoMarketV1RegistryV2and twoMorphoVaultV1Registrycontracts.RegistryListcannot remove a sub-registry, but its owner can add one; at the observed block that owner was a Safe 1.3.0 with a threshold of 6 out of 10 owners, so the families of adapters this vault may ever adopt are decided by that Safe and not by the curator alone. - Underlying markets and their oracles. The vault reads no price itself. Every price enters through the Morpho Blue markets the adapter supplies to (ten market ids on the observed adapter
0x3C6EAB22cFc03fc32AD1bf836f2F886A5015DFFF), each with its own oracle and liquidation threshold. The PT-reUSD Liquidations on Morpho shows such an oracle at work: a 320,000 SY trade moved a TWAP-based PT feed and liquidated 36.1M of loans. That incident left no bad debt; the same mechanism on collateral that liquidators cannot absorb would leave some with the markets’ suppliers, among them any vault holding them (derived). - A MetaMorpho vault held through
MorphoVaultV1Adapter. The vault then inherits that vault’s curator, timelocks and markets, and the monitoring article notes that Vault V1.1 does not realise bad debt. Morpho Vault V2 Adapters covers the caveats of that adapter. - Wrappers built on top. A product that wraps a Vault V2 adds its own dependencies. Risk of a Zama Confidential Morpho Vault analyses one: the vault underneath is unchanged, but exits now also depend on the decryption service.
- The base chain. The in-kind exit needs a flash loan of the asset and a transaction included in time. When a borrower in a held market is underwater, the order in which exits and the liquidation land on Ethereum decides who carries the loss (derived, from the window described in the monitoring article). The availability of flash liquidity for the vault’s asset was not checked (see Gaps).
Depeg
- A share has no peg. The share price follows total assets, and losses pass through at the next accrual (Total assets and the max rate). Nothing in the vault holds a share at a fixed value of its asset.
- The vault’s asset. Every amount is in the vault’s single asset, RLUSD for the observed vault, and nothing in the contracts converts or hedges it. A depeg of the asset is carried in full by depositors who measure their position in another unit. RLUSD’s backing and redemption terms are outside the sources of this article (see Gaps).
- Pegged collateral in the held markets. A depeg of a collateral token (a synthetic dollar, a liquid staking token, a PT) becomes bad debt when the fall outruns liquidations. With the Morpho Blue liquidation incentive factor, a position liquidated exactly at its LLTV still covers its debt plus the incentive only while the collateral falls by less than \(d\) (derived; the formula comes from Morpho’s documentation, see Gaps):
For the 91.5 % LLTV of the USDe / RLUSD market the monitoring article observed, LIF is about 1.0262 and \(d\) about 6.1 %. A collateral that gaps down by more than that between two blocks leaves bad debt even if liquidators act at once.
Collateral and market risk
- Concentration is a curator choice. The collateral id lets a cap limit one collateral across every market that accepts it (Ids and caps), but a cap set high limits nothing. On 10 October the monitoring article found 51.3 % of the observed vault in a single kBTC market.
- Reflexive loops sit in the markets. Liquidations sell collateral into the venues that set the oracle price, which can push the price further and trigger more liquidations, as in the PT-reUSD case above. The vault supplies the loan side, so it is exposed only to what liquidations fail to cover, but that is exactly where a loop ends (derived).
- Caps are soft after allocation. Interest, donations and withdrawals move allocations above their caps without a revert (Ids and caps), so a cap is a limit on new money rather than on current exposure.
Liquidity
- A plain withdrawal pays from idle assets and the liquidity adapter only (Deposits and withdrawals). At the observed block the vault had no liquidity adapter and held 36.08M RLUSD idle out of 478.11M, 7.55 % of total assets; any further exit had to go through
forceDeallocate. - The forced exit is bounded by the markets.
forceDeallocateonly withdraws what a market has not lent out, and an in-kind redemption leaves the depositor with a supply position that is as illiquid as the market (derived). Its penalty on the observed adapter was 0.01 % (1e14in WAD), below the 2 % ceiling. - Instant allocator settings. An allocator can change or unset the liquidity adapter without delay (Roles), which can make
withdrawrevert; it cannot blockforceDeallocate. - Gates on the exit path. Three of the four gates can trap funds (Gates). On the observed vault all four gates were unset and the setters of the three exit-related gates were abdicated, so no gate can ever block an exit there. The
sendAssetsGatesetter, which restricts entry only, was neither abdicated nor timelocked. - The race before a write-off. Depositors who exit while a borrower is underwater but not yet liquidated leave the loss to those who stay (ChainSecurity finding CS-MORPHO-VLT2-001, described in the monitoring article). Liquidity therefore favours the depositor who watches.
Centralisation and governance
The roles of the observed vault, read with owner(), curator(), isSentinel and isAllocator at block 26,248,478. Sentinels and allocators were listed from every SetIsSentinel and SetIsAllocator event since the vault’s creation, then confirmed one by one; the Safes were read with getThreshold(), getOwners(), getModulesPaginated and the guard storage slot.
| Role | Address | What it is |
|---|---|---|
| Owner | 0xe8C9C99EcaD0686A14A00d8521c572281E938008 |
Safe 1.4.1, threshold 1 of 1 (owner 0x295Df6B70f4Ed2bE704Bd802a4D805fE700Bd7E9, an EOA), no module, no guard |
| Curator | 0x9e396dE3312D373b87F9BD8763fb48184b42aac0 |
Safe 1.4.1, threshold 1 of 1 (owner 0x992592e073AA9b98daeD79eE05C4Af832e1D0ABE, an EOA), no module, no guard; also a sentinel and an allocator |
| Sentinel and allocator | 0x13DE0cEE0B83562CBfD46682e10FfA4E3c5090e1 |
The vault’s deployer, an EOA carrying an EIP-7702 delegation |
| Sentinel | 0x74D1672d0b90F6E7c322dEc9D5396fF6ACE61618 |
An EOA |
| Allocator | 0xC4Ba4e822C420452fe2BAB93211208D3CcBd79D3 |
An EOA |
- Owner powers are instant.
setOwner,setCuratorandsetIsSentinelhave no timelock. The owner can replace the curator in one transaction; the new curator still goes through the timelocks, and a change submitted by the former curator stays executable, becauseexecutableAtis keyed by the calldata and not by who submitted it (derived, fromsubmitandtimelocked). Both the owner and the curator are Safe accounts with one signer, so a single key controls each role and the multisig adds no second approval. - What is timelocked on the observed vault. Three days (259,200 s):
addAdapter,increaseAbsoluteCap,increaseRelativeCap, andburnShareson the adapter. Seven days (604,800 s):removeAdapter,increaseTimelockandabdicate. Lowering any of these timelocks takes as long as the timelock itself (Timelocks and abdication). - What is instant on the observed vault. Timelock zero, so a submission can be executed in the same block:
setIsAllocator,setPerformanceFee,setManagementFee, both fee recipients,setForceDeallocatePenaltyandsetSendAssetsGate. Instant by design: cap decreases andrevoke(curator and sentinels),deallocate(allocators and sentinels),allocatewithin the caps,setLiquidityAdapterAndDataandsetMaxRate(allocators), and the owner functions. Abdicated:setAdapterRegistryand the three exit-related gate setters. - The non-custodial guarantee covers adapters and caps here, not fees. A curator key could, without notice, raise the management fee from 0 to 5 % a year, about 23.9M RLUSD a year or 65.5K a day on 478.11M of assets, raise the performance fee from 10 % to 50 %, or raise the force-deallocation penalty from 0.01 % to 2 %, which turns the cost of a forced exit of 1M RLUSD from 100 into 20,000 (derived). It could not add an adapter or raise a cap without three days of notice, nor block an exit with a gate.
- Off-chain operators. Allocation decisions, the choice of markets and the response to an incident are taken off-chain by whoever holds the allocator and curator keys. Morpho’s API and front end report warnings on vaults, but they are a service, not part of the contracts.
Loss sharing
- No first-loss layer. A loss is spread over all holders in proportion to their shares (Losses). There is no insurance fund, no junior tranche and no priority between depositors.
- Depositors take losses without having borrowed. Every depositor is a supplier to the markets the curator and allocators chose, and the adapter’s
burnSharesrealises a loss on a curator decision after its timelock (three days on the observed adapter). - The
maxRatebuffer exists only when it is used. Undistributed gains absorb a later loss only ifmaxRateis set below the real rate. On the observed vault it was 63,419,583,967 per second in WAD, which is 200 % a year, the ceiling, so no buffer builds up by design (derived). - What the remaining holders receive. Force-deallocation penalties stay in the vault as a donation to those who remain, and the management fee is charged whether the vault gains or loses (Fees).
- Pooled holders. A wrapper that holds the vault for many users, such as the Zama confidential vault linked above, passes the loss on to its own users as a pool.
Regulatory
- The vault contract. None found that the vault’s design raises by itself: it has no issuer and needs no identity check unless a gate is set. Gates can turn a vault into a permissioned one (Gates); on the observed vault the share and exit gates can no longer be set, while the entry gate still can.
- The asset. A vault denominated in a regulated stablecoin carries that stablecoin’s regulatory position. Whether and how RLUSD is classified, for example as an e-money token in the sense of Stablecoins Under MiCA, is outside the sources of this article (see Gaps).
Summary
| Risk | Where it comes from | What the design does about it | What remains |
|---|---|---|---|
| Contract bug | Immutable VaultV2 and adapters |
Several audits, formal-verification specs, no upgrade path | A bug cannot be patched; deployed bytecode not compared with the pinned commit |
| Adapter misreporting or reverting | realAssets() trusted at face value |
Registry restricts adapter families; adding one is timelocked | One reverting adapter halts the vault; the registry owner can add families |
| Oracle or collateral failure | Each held Blue market’s oracle and LLTV | Collateral caps across markets | Bad debt when collateral gaps beyond about 6.1 % at 91.5 % LLTV |
| Asset depeg | The vault’s single asset | None | Carried in full by depositors |
| Illiquidity | Idle balance and liquidity adapter only | forceDeallocate and in-kind redemption, penalty capped at 2 % |
Bounded by unborrowed market liquidity; race before a write-off |
| Governance change | Owner, curator, allocators, sentinels | Per-selector timelocks, abdication, sentinels can revoke | Owner and curator are one-signer Safes; fees and penalty have no timelock on the observed vault |
| Loss allocation | Pro rata over shares | maxRate buffer if used, penalties donated to remaining holders |
No first-loss layer; observed maxRate at its ceiling |
| Regulatory | Gates and the asset’s status | Optional permissioning through gates | Asset classification outside the sources |
Frequently Asked Questions
Q: What does an adapter return to the vault, and why does the vault need both values?
On allocate and deallocate, an adapter returns a list of ids and the signed change in the value of the position. The vault adds the change to the allocation of each id, then checks the caps of each id. The ids say which risk factors the position belongs to, and the change says by how much the exposure to each of them moved. Separately, realAssets() gives the total value the adapter holds, which the vault uses to compute interest and losses.
Q: Why is the relative cap measured against firstTotalAssets rather than the current total assets?
If it were measured against the live total, an allocator could flash-borrow a large amount, deposit it, allocate a large position against the inflated total, then withdraw and repay within the same transaction, leaving an allocation far above the intended fraction. firstTotalAssets is fixed at the first accrual of the transaction, so a deposit made later in the same transaction does not raise the base. The source notes that this makes the check conservative: a large deposit routed through the liquidity adapter can revert even when it would be within the cap afterwards.
Q: A curator has set a seven-day timelock on addAdapter. How long does it take to add an adapter, and how long to reduce that timelock to one day?
Adding an adapter takes seven days from the submit call. Reducing the timelock also takes seven days, because the delay applied to decreaseTimelock(addAdapter, 1 day) is the current timelock of addAdapter, not that of decreaseTimelock. A curator therefore cannot shorten a delay faster than the delay itself, which gives depositors the same notice for the reduction as for the action it protects.
Q: Real interest across the markets is 8 % per year but the vault reports 5 %. What could explain it, and what happens to the difference?
The most likely explanation is a maxRate set at 5 % per year. accrueInterest takes the minimum of the real assets and the stored total grown at maxRate, so the excess is not distributed and stays in the vault as unaccounted value. It is distributed later, at most at maxRate, when real growth falls below the cap, and it acts as a buffer: a later loss smaller than the buffer reduces real assets without lowering them below the distributed total. The performance fee is computed on the distributed 5 %, not the real 8 %, and the management fee, taken on total assets, also reduces the net rate.
Q: A vault’s only liquid market has no liquidity left and allocators are not acting. How can a depositor exit, and what does it cost?
The depositor can perform an in-kind redemption:
- Flash-borrow an amount of the asset and supply it to the market the vault is invested in, on their own account.
- Call
forceDeallocateon that market’s adapter for the same amount, which withdraws the liquidity just added back to the vault’s idle balance and burns the penalty from the depositor’s shares. - Call
withdrawto take the amount from the idle balance, and repay the flash loan.
The depositor ends up with a supply position in the market instead of vault shares. The cost is the penalty set for that adapter, at most 2 %, plus gas. The penalty stays in the vault and benefits the remaining depositors.
Q: Why do the four max* functions return zero, and why does the vault still claim ERC-4626 compliance?
ERC-4626 allows max* functions to underestimate, and returning zero is the extreme case. The vault cannot know in a revert-free view whether a gate will accept an address, whether a cap leaves room, or whether the liquidity adapter can pay, so any non-zero value could be wrong. The consequence is practical: aggregators and routers that rely on max* will treat the vault as closed unless they compute limits themselves.
Q: Which roles can reduce risk in an emergency, and which actions are available to them immediately?
Both the curator and the sentinels can act without delay. They can decrease absolute and relative caps, which stops new allocations to a market or collateral, and revoke any pending curator change. Sentinels can also call deallocate to pull funds from an adapter to idle. Allocators can deallocate too, and can set the liquidity adapter, but they cannot change caps. None of these roles can add risk without a timelock.
Gaps
The points below were not settled by the sources of this article and should be checked before relying on its risk section.
| Gap | Why it matters | What to read or verify |
|---|---|---|
| The observed vault was created on 4 March 2026, before the commit analysed here, and its verified source was not compared with that commit. | A behaviour described from the pinned commit may differ in the deployed vault. | Diff the Etherscan source of 0x6dC58a0FdfC8D694e571DC59B9A52EEEa780E6bf against src/VaultV2.sol at d992cb8, and identify the release it was built from. |
Who holds the single keys behind the owner and curator Safes, and who the ten owners of the RegistryList Safe are, is not established. |
The governance risk of the observed vault rests on these keys. | Sentora’s and Morpho’s published disclosures, and the history of 0x295Df6B70f4Ed2bE704Bd802a4D805fE700Bd7E9, 0x992592e073AA9b98daeD79eE05C4Af832e1D0ABE and 0xcBa28b38103307Ec8dA98377ffF9816C164f9AFa. |
The EIP-7702 delegate of the deployer account 0x13DE0cEE0B83562CBfD46682e10FfA4E3c5090e1 (0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B) was not identified. |
That account is both a sentinel and an allocator, and its delegate code decides who can make it act. | getsourcecode on the delegate address and its execution permissions. |
| Morpho’s documentation risk pages and the curator’s own risk disclosure for this vault were not read. | They may document risks or commitments, such as minimum timelocks, that this section does not cover. | The risk sections of docs.morpho.org and the vault page on the Morpho app. |
The liquidation incentive formula used in the Depeg subsection comes from Morpho Blue documentation, not from the vault-v2 source. |
The 6.1 % margin depends on it. | liquidate in morpho-org/morpho-blue src/Morpho.sol and its constants. |
| The flash liquidity available in RLUSD for an in-kind redemption was not checked. | Without a flash loan of the asset, the in-kind exit described above cannot be performed. | RLUSD balances of flash-loan sources, including the Morpho Blue singleton 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb. |
| RLUSD’s backing, redemption terms and regulatory classification are not in the sources. | A depeg or a regulatory event on the asset is carried in full by depositors. | The issuer’s published attestations and terms, and its status under MiCA and US rules. |
| The Morpho bug bounty, listed in a reference note as 2.5M USD on Cantina, was not checked. | A bounty is one of the few remedies for a bug in immutable code. | Morpho’s security page and the Cantina bounty listing. |
References
Analyzed source
- morpho-org/vault-v2 analyzed at commit
d992cb8438b8630b3fee4649311d78d649264a5e(main, 568 commits after the2026-08-13tag), 2026-10-10
Morpho documentation
- Morpho Vaults V2 contract reference
- MorphoMarketV1AdapterV2 reference
- MorphoVaultV1Adapter reference
- Blue public allocator reference
- Assets flow, including deposit and withdrawal limits
- Vault V2 audit reports in the repository
Standards
- ERC-4626 - Tokenized Vaults
- ERC-2612 - Permit Extension for EIP-20 Signed Approvals
- EIP-712 - Typed structured data hashing and signing
- EIP-1153 - Transient storage opcodes
- OpenZeppelin - ERC-4626 inflation attack