From One Standard to Three Yield Engines
DOVs, Perps, and how ERC-4626 ties DeFi yield together
A single mental model: deposit an asset, receive a tokenized share, and let a strategy make that share worth more over time. Option vaults, lending markets, and perp liquidity pools all run on this exact plumbing — they only differ in the engine that grows the pool. This primer walks the standard first, then each engine, and shows how they connect.
0. The one diagram: same chassis, three different engines

The core in the middle is identical no matter which engine you plug in. deposit() mints shares; the strategy grows totalAssets; pricePerShare rises; redeem() returns principal plus accrued yield. Everything below is just "what is the engine, and how does its risk differ."
1. ERC-4626 in five minutes
ERC-4626 is the tokenized vault standard. It wraps a single underlying asset and represents each depositor's claim as a fungible ERC-20 share. That standardization is why aggregators, front-ends, and other contracts can integrate any vault without custom glue.
The core interface
| Function | What it does |
|---|---|
asset() |
Address of the underlying token the vault holds (e.g. ETH, USDC). |
deposit(assets, receiver) |
Pulls assets in, mints shares to receiver. |
mint(shares, receiver) |
Same, but you specify the shares you want out. |
withdraw(assets, receiver, owner) |
Burns shares, returns a specific amount of assets. |
redeem(shares, receiver, owner) |
Burns a specific number of shares, returns the assets they're worth. |
totalAssets() |
Total underlying currently controlled by the vault. |
convertToShares / convertToAssets |
The exchange-rate helpers. |
The one formula
$$\text{pricePerShare} = \frac{\text{totalAssets}}{\text{totalSupply}}$$
Yield happens whenever totalAssets grows while totalSupply (shares outstanding) stays the same. The share silently appreciates. No rebasing, no reward tokens to claim — the number of shares in your wallet is constant, but each one redeems for more.
Worked example
- Ten users deposit 100 ETH into a fresh vault →
totalAssets = 100,totalSupply = 100, so 1 share = 1 ETH. - The strategy earns 5 ETH (a premium, interest, or funding — pick your engine) →
totalAssets = 105,totalSupplystill100. convertToAssets(1 share)= 105 / 100 = 1.05 ETH.- A user calling
redeem(10 shares)burns them and receives 10.5 ETH.
The one security footgun: the inflation / donation attack
Because the rate is totalAssets / totalSupply, an attacker who is the first depositor can mint 1 wei of shares, then donate a large amount of the asset directly to the vault (bypassing deposit). This inflates pricePerShare so the next depositor's rounding-down mint gives them zero shares, and the attacker redeems the victim's deposit. Mitigations: seed the vault with dead shares at deploy, use "virtual shares/assets" offsets (OpenZeppelin v4.9+ does this), or require a minimum first deposit. Keep this in mind for every engine below — it's a property of the standard, not of any one strategy.
// Minimal ERC-4626 vault (OpenZeppelin). The strategy is whatever moves `totalAssets`.
contract YieldVault is ERC4626 {
constructor(IERC20 asset_) ERC4626(asset_) ERC20("Vault Share", "vSHARE") {}
// deposit(), mint(), withdraw(), redeem(), convertToAssets() are all inherited.
// The ONLY thing a strategy must do to pay yield is grow totalAssets():
// - a DOV collects an option premium -> asset balance goes up
// - a lending wrapper accrues interest -> aToken balance goes up
// - a perp LP vault earns funding/fees -> collateral balance goes up
// Shares are untouched, so pricePerShare rises for everyone. That's the whole trick.
function totalAssets() public view override returns (uint256) {
// Override to report principal + whatever the strategy is currently worth.
return _deployedCapital() + IERC20(asset()).balanceOf(address(this));
}
}
2. Engine A — Decentralized Option Vaults (DOVs)
A DOV automates an options-selling strategy into deposit-and-earn. The vault sells options, collects the premium, and that premium is the yield. It runs on a recurring epoch (weekly or bi-weekly).

Lifecycle of one epoch
- Deposit window — users deposit the underlying. A covered-call vault takes a volatile asset (ETH); a cash-secured-put vault takes a stablecoin (USDC).
deposit()mints shares. - Strike selection & minting — the epoch starts, collateral is locked, an automated mechanism (or manager via oracle) picks an out-of-the-money strike (e.g. +10% for a weekly call), and the vault mints the option through an options protocol (Opyn, Ribbon, Hegic).
- Sell for premium — the option is sold to market makers. The premium is paid upfront and lands back in the vault →
totalAssets ↑, supply flat →pricePerShare ↑. - Settlement at expiry, two outcomes:
- ITM / exercised — price moved through the strike; the vault pays out collateral →
totalAssets ↓→ net loss. - OTM / worthless — strategy won; the vault keeps 100% of collateral plus the premium.
- Rollover / exit — the cycle auto-repeats; anyone exiting calls
redeem()/withdraw()to burn shares for principal + accumulated premiums.
Covered call vs cash-secured put
| Covered Call Vault | Cash-Secured Put Vault | |
|---|---|---|
| Deposits | Volatile crypto (ETH) | Stablecoin (USDC) |
| Outlook | Moderately bullish / sideways | Moderately bearish / sideways |
asset() returns |
ETH address | USDC address |
| If price rips up | Gets "called" — upside capped at strike | Max profit, keeps full premium |
| If price crashes | Holds a depreciating asset (USD loss) | Forced to buy the asset at a loss with USDC |
Risks
- Negative skewness — many small consistent premium wins, punctuated by rare large losses in high-vol events. You are short volatility.
- Locked liquidity — most DOVs block mid-epoch withdrawals because collateral is tied up in an open option (see §3 for how ERC-4626 copes).
- Smart-contract / oracle risk — depends on the options protocol and on price oracles (Chainlink) to settle strikes.
3. How ERC-4626 implements a DOV — the connective tissue
This is where the standard and the strategy meet. The DOV is not a separate technology; it is an ERC-4626 vault whose totalAssets is moved by option premiums and payouts.
The mapping, function by function:
| DOV concept | ERC-4626 mechanism |
|---|---|
| "What do I deposit?" | asset() returns ETH (covered call) or USDC (CSP). |
| Getting a claim on the pool | deposit() mints shares pro-rata at the current pricePerShare. |
| Premium becomes yield | Premium increases the vault's asset balance → totalAssets() returns more → every share appreciates. Supply is untouched. |
| Loss on an exercised option | Collateral leaves the vault → totalAssets() returns less → shares depreciate. Same accounting, opposite sign. |
| Exiting with your gains | redeem(shares) burns shares for convertToAssets(shares) = principal + net premiums. |
// DOV-flavoured overrides. Everything is standard ERC-4626 except two hooks.
contract CoveredCallVault is ERC4626 {
uint256 public lockedCollateral; // tied up in the live option this epoch
uint256 public premiumBuffer; // premiums collected, waiting to settle
// Yield/loss enter the system ONLY by changing this number.
function totalAssets() public view override returns (uint256) {
return IERC20(asset()).balanceOf(address(this)) // idle collateral
+ lockedCollateral // collateral in the option
+ premiumBuffer; // premiums already earned
}
function startEpoch(uint256 strike) external onlyManager {
// lock collateral, mint + sell the option, receive premium
lockedCollateral = IERC20(asset()).balanceOf(address(this));
uint256 premium = _mintAndSellOption(strike, lockedCollateral);
premiumBuffer += premium; // <-- totalAssets just went up => pricePerShare up
}
function settleEpoch() external onlyManager {
(bool exercised, uint256 payout) = _settleOption();
if (exercised) lockedCollateral -= payout; // <-- totalAssets down => loss booked
premiumBuffer = 0;
lockedCollateral = 0; // collateral returns to idle balance
}
}
The one real friction: instant-liquidity assumption
Vanilla ERC-4626 assumes withdraw/redeem can settle synchronously — the assets are always there. A DOV violates this during the epoch because collateral is locked inside a live option. Two production patterns solve it:
- Windowed withdrawals — the vault only honours
redeem()between epochs; mid-epoch calls revert. Simple, but capital is illiquid for up to a week. - Request-based / async vaults (ERC-7540) — an extension of ERC-4626 that splits withdrawal into
requestRedeem()(queue it now) and a laterredeem()(claim once the epoch settles). This is the emerging standard for any vault whose strategy can't return funds on demand — DOVs, RWA vaults, and cross-chain vaults all use it.
Takeaway: a DOV is "ERC-4626 + an option strategy in totalAssets + an async-withdrawal shim." Nothing more exotic.
4. Engine B — Perpetual Futures (Perps)
Perps deserve their own section because they are the one piece here that is not itself an ERC-4626 vault — yet they show up inside ERC-4626 vaults constantly. Understand the mechanic first.

Mechanics
- Open a position — deposit margin (collateral, e.g. USDC), pick leverage (
notional = margin × leverage), go LONG (profit if price ↑) or SHORT (profit if price ↓). - Anchoring to spot without expiry — a perp never expires, so it needs a force pulling its price to spot. That's the funding rate, exchanged every 1h/8h between longs and shorts:
Mark > Index(perp trading rich) → funding positive → longs pay shorts.Mark < Index(perp trading cheap) → funding negative → shorts pay longs.- Either way, the economic incentive drags the perp back toward the spot index price (fed by an oracle).
- Risk & liquidation — unrealized PnL is marked-to-market continuously. If margin drops below the maintenance margin, the engine liquidates the position. Losses beyond the collateral hit the insurance fund; if that's exhausted, auto-deleveraging (ADL) force-closes profitable traders as a last resort.
Risks
- Liquidation from leverage, sustained funding cost, oracle/index manipulation, and socialized losses via ADL.
Where ERC-4626 comes back in
A single perp position is one trader's individual bet — not a pooled share, so it isn't ERC-4626. But the liquidity that stands on the other side of every trader usually is a tokenized vault:
- LP / market-maker vaults (GLP-style on GMX, HLP on Hyperliquid): depositors pool collateral, the pool is the counterparty to all traders, and it earns funding + trading fees minus trader PnL. That net flow moves
totalAssets, and depositors hold vault shares. This is Engine C in the overview diagram. - Delta-neutral vaults: an ERC-4626 vault that holds spot and a short perp of equal size, harvesting funding while hedging price. The share appreciates from funding income; the strategy just happens to live partly in a perp.
So perps are both a standalone product and a component other ERC-4626 vaults wrap for yield.
5. Engine C — ERC-4626 in lending protocols
Lending is the cleanest example of ERC-4626 as a pure yield wrapper, and it predates DOVs in the standard's adoption.
The mechanic
You supply an asset to a money market (Aave, Compound, Morpho). Borrowers pay interest. That interest accrues to suppliers, so the supplier's claim grows over time — exactly the totalAssets ↑, supply flat pattern.
- Aave issues
aTokens(aUSDC, aWETH) that rebase to reflect accrued interest, and Aave v3 now ships ERC-4626 vaults on top so integrators get the standard interface. - Morpho / MetaMorpho vaults are natively ERC-4626: you deposit into a curated vault, it allocates across underlying lending markets, and
pricePerShareclimbs with interest. - Yearn popularized the pattern — a "yVault" is an ERC-4626 share whose
totalAssetsis deployed into whatever strategy earns the most, lending included.
// A lending wrapper is even simpler than a DOV: no epochs, no options, no async exit.
contract LendingVault is ERC4626 {
IPool public immutable aavePool;
function totalAssets() public view override returns (uint256) {
// aToken balance already includes accrued interest -> totalAssets grows passively.
return aToken.balanceOf(address(this));
}
function _deposit(address caller, address receiver, uint256 assets, uint256 shares)
internal override
{
super._deposit(caller, receiver, assets, shares);
aavePool.supply(asset(), assets, address(this), 0); // put capital to work
}
// Withdrawals are synchronous: lending liquidity is (usually) available on demand,
// so no ERC-7540 request queue is needed — the key contrast with a DOV.
}
Why it matters for the comparison
Lending is the low-skew engine: yield is small and steady, and the tail risk is bad debt (a borrower position going underwater faster than it can be liquidated) or a utilization spike freezing withdrawals — not a short-volatility blow-up like a DOV. Same chassis, very different risk shape.
6. Putting it together
The whole point: one standard, three engines, three risk profiles.
| DOV | Lending vault | Perp LP vault | |
|---|---|---|---|
| You deposit | ETH or USDC | Any supported asset | USDC / basket collateral |
| Yield source | Option premiums | Borrower interest | Funding + fees − trader PnL |
What grows totalAssets |
Premium collected each epoch | Interest accrued continuously | Net flow from being the house |
| Liquidity | Locked per epoch (often ERC-7540 async) | Usually on-demand | On-demand, subject to utilization |
| Skew / tail risk | Negative (short vol; rare big losses) | Low (bad debt, utilization) | Adverse selection vs skilled traders |
| Example protocols | Ribbon, Thetanuts, Opyn | Aave, Morpho, Yearn | GMX (GLP), Hyperliquid (HLP) |
If you remember one thing: ERC-4626 is the accounting, the strategy is the engine. pricePerShare = totalAssets / totalSupply is true in all three; the only question a depositor should ask is "what makes totalAssets go up, and what makes it crash?"