Security Considerations When Integrating ERC-4626 Vaults
Practical guidance for app developers integrating tokenized vaults, using Morpho Vaults as a case study
If you’re building an app that accepts user deposits and generates yield, you’ll likely consider integrating one or more yield-generating vaults. Fortunately, many modern vaults adhere to the ERC-4626 standard, which greatly simplifies the integration process. Thanks to this unified interface, supporting multiple vaults — or adding new ones later — becomes significantly easier in terms of both implementation and maintenance.
However, despite the shared interface, vaults can behave differently under the hood. Each has its own accounting logic and yield strategy, and the ERC-4626 standard allows for a fair degree of flexibility in implementation. The standard defines only minimal required behaviors, leaving many details up to individual vault developers.
This means that you need a solid understanding of a vault’s behavior to build secure and reliable integrations. If preserving users’ principal is a high priority, it is especially important to understand how vaults handle losses and temporary withdrawal unavailability, so that users can withdraw funds reliably and avoid unexpected losses.
In this post, we’ll explore some of the behavioral differences between different ERC-4626 vault implementations using Morpho Vaults as a case study. Morpho Vaults is one of the widely used ERC-4626 vault implementations and offers valuable insight into the nuances and trade-offs developers may encounter.
ERC-4626 vault basics
At a high level, users deposit assets into a vault, and receive a tokenized deposit receipt that entitles them to their portion of the vault's assets. They can withdraw assets in exchange for these deposit receipts, even shortly after depositing. Where vaults are designed to accumulate value, if the vault grows its assets over time, the depositor’s portion becomes more valuable. Of course, like with any investment, there’s always a chance of losses too.
Vaults generate yield using a variety of decentralized and autonomous mechanisms that are not controlled by any person, including DEXs like Uniswap or Curve (via swap or staking fees) and lending protocols like Aave or Morpho (via borrower interest payments). Vaults autonomously deposit user funds into these protocols as liquidity providers (LPs) or lenders, then use the proceeds to increase the value of users’ ownership of the vault.
Morpho Vaults, for instance, use Morpho’s lending markets as their underlying yield source. These vaults serve as ERC-4626-compliant wrappers around those lending protocols.
Principal risk and accounting behavior
A key consideration when integrating vaults is understanding how they handle losses from their underlying yield sources. This is especially important when preserving principal is a high priority.
Some types of yield sources, like lending markets, are generally not expected to incur losses, except during extreme market volatility or price manipulation schemes. When losses do happen, different vault implementations show them in different ways.
Morpho Vaults v1 and v1.1 show different behaviors in how they surface losses, reflecting patterns seen in many other production vaults. Below, we’ll use these two versions as a lens for understanding different vault behaviors and the trade-offs involved in different design choices. We’ll also outline recommended practices for dealing with each type of behavior safely and reliably.
Morpho Vault v1: Immediate loss realization
In version 1, the price of an ownership stake in a vault immediately reflects losses in the underlying yield source. For example, if a bad debt event occurs—typically due to a sharp drop in borrower collateral value—the price drops accordingly.
These losses may result from volatile long-tail assets or compromised oracle prices. While some are temporary, users who withdraw during this time will realize their losses.
To protect against this, integrations should implement safeguards, such as slippage protection, to prevent withdrawals below a minimum expected value. A common pattern is to require users to specify minimum acceptable withdrawal proceeds, effectively setting a minimum price they are willing to accept—similar to placing a limit order. If the actual amount falls below this threshold, the transaction is reverted.
However, your app should offer both protected and unprotected order mechanisms, allowing the user to express their intent accordingly. In some scenarios, such as market-wide failure, users may prefer to withdraw immediately so they can quickly exit and avoid further losses. For example, allowing users to set a minimum withdrawal threshold of zero gives users full control over whether to proceed, even if they’re accepting a potential loss.
Morpho Vault v1.1: Unrealized losses
In contrast, version 1.1—like most lending pools, including Aave—does not reflect losses in the price of an ownership stake in a vault. Even if the vault’s assets decline, the price remains the same. While this design protects users from short-term oracle manipulation, it introduces the risk of uneven loss distribution, where early withdrawers may receive full value, while late withdrawers could receive significantly less—or even nothing—if the vault becomes undercollateralized. This can lead to bank runs when users realize the vault’s nominal unit value is not backed by actual assets.
To mitigate this, vault owners are expected to cover bad debt using external funds. However, this recovery often happens off-chain and can take time, leaving the vault undercollateralized in the meantime.
To safely integrate with v1.1 vaults, your app should detect whether such losses have occurred and whether they have been covered by the vault owners:
- Use
lostAssets()(available only in v1.1) to detect historical losses. - Check the balance of
address(1), which is used by vault operators to “donate” assets and cover losses. If its balance exceedslostAssets(), the vault is fully backed.
If the vault hasn’t recovered the losses, consider implementing emergency measures to protect users from potential bank run scenarios.
Temporary withdrawal limitations (without losses)
Even without losses, withdrawals may be temporarily unavailable, especially when the vault uses a lending protocol as its yield source.
Unlike DEX-based vaults, lending-based vaults can only allow withdrawals up to the liquidity currently available in the lending pool. If most capital is borrowed, users will have to wait for loan repayments before they can withdraw.
This limitation is not a bug, but built into lending-based vaults by design—similar to fractional reserve banking. Although vault operators often maintain buffer liquidity, users making larger withdrawals may still experience a delay. This constraint applies to both v1 and v1.1 of Morpho Vaults.
Integrations should account for this, particularly in scenarios like vault migrations that require full fund withdrawal. If liquidity is low at the time of migration, a full withdrawal may fail, and your app may only be able to withdraw some of the amount. In these cases, the migration process may need to happen in multiple steps over time, rather than as a single transaction. To handle this, design your migration logic to support multi-step withdrawals and retries over time.
Interface-level considerations
Based on the behaviors outlined above, here are key considerations when using ERC-4626 interface functions with Morpho Vaults:
Convert Functions
These functions reflect the spot price of vault ownership.
- In v1, vault prices can decrease due to losses, so apps must be able to handle this volatility by implementing slippage protection or validating price assumptions before executing transactions.
- In v1.1, vault prices are monotonic and do not decrease (except for rounding errors), but they may not reflect true asset values. Apps should account for this by independently verifying the vault’s backing—using checks like
lostAssets()—rather than relying solely on vault price as an indicator of value.
Watch for potential price inflation attacks, especially in low-liquidity vaults where prices can spike artificially — in particular when integrating with new or thinly capitalized vaults. The issue is well-studied, with several proposed mitigations, including by OpenZeppelin and Morpho. One effective approach is initializing the vault with a small seed deposit that remains permanently locked. This helps stabilize the initial vault price and prevents price manipulation in low-liquidity scenarios.
Preview functions
Use preview functions strictly for estimation—not for execution. These functions estimate the result of a deposit or withdrawal but do not guarantee success.
For example, previewRedeem(balanceOf(user)) gives a theoretical value, but redeem(balanceOf(user)) could still fail due to rounding or insufficient liquidity.
Max functions
These functions indicate the maximum transaction size that can be executed at a given time.
- Deposit limits are constrained by the caps of the underlying Morpho markets.
- Withdrawal limits depend on current liquidity.
These simulate actual transaction logic and are generally accurate, except for rounding. Behavior is consistent across both v1 and v1.1.
For example, due to these limitations, maxRedeem(user) and maxWithdraw(user) may be significantly less than balanceOf(user) and previewRedeem(balanceOf(user)), respectively.
Use preview functions to estimate values, and max functions to determine whether an action is currently feasible.
Deposit and withdraw functions
When executing transactions, account for potential changes in vault price between submission and execution, as price shifts can expose users to unintended losses. To mitigate that risk, implement slippage protection using min/max thresholds, and revert transactions if the resulting values fall outside these bounds.
While vault prices in v1.1 do not decrease, apps that support both versions — or other vaults — should adopt conservative safeguards. Libraries such as Morpho’s ERC4626Bundler offer this protection out of the box.
Rounding errors
Rounding errors are inevitable across all ERC-4626 interface functions, as they involve computing vault prices. The magnitude and direction of these errors can vary between functions and vault implementations, and it’s important to understand these subtle details. This post does not cover rounding behavior in depth, as this is already well-studied within the ERC-4626 context. For a deeper exploration, refer to the ERC-4626 standard and associated property-based tests.
Additional edge cases
There are other edge cases we haven’t covered here. For example, in v1.1, lostAssets() may still return zero even when bad debt exists but hasn’t been acknowledged. In such cases, the vault may appear healthy when it is not. Detecting these cases may require simulating the Morpho market’s internal accounting logic.
Depending on your app’s risk tolerance, these subtleties could be crucial. Consulting vault documentation and resources — such as the Morpho docs or audit reports — might help clarify these behaviors.
Always design your integration logic with an understanding of how vaults handle losses, liquidity constraints, and price volatility. As ERC-4626 vaults become a foundational part of the DeFi stack, this knowledge is essential for building secure, reliable applications.