Skip to content

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

ERC-4626 as a common chassis

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

  1. Ten users deposit 100 ETH into a fresh vault → totalAssets = 100, totalSupply = 100, so 1 share = 1 ETH.
  2. The strategy earns 5 ETH (a premium, interest, or funding — pick your engine) → totalAssets = 105, totalSupply still 100.
  3. convertToAssets(1 share) = 105 / 100 = 1.05 ETH.
  4. 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).

DOV epoch lifecycle

Lifecycle of one epoch

  1. 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.
  2. 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).
  3. 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 ↑.
  4. Settlement at expiry, two outcomes:
  5. ITM / exercised — price moved through the strike; the vault pays out collateral → totalAssets ↓ → net loss.
  6. OTM / worthless — strategy won; the vault keeps 100% of collateral plus the premium.
  7. 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 later redeem() (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.

Perp market mechanics

Mechanics

  1. Open a position — deposit margin (collateral, e.g. USDC), pick leverage (notional = margin × leverage), go LONG (profit if price ↑) or SHORT (profit if price ↓).
  2. 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:
  3. Mark > Index (perp trading rich) → funding positive → longs pay shorts.
  4. Mark < Index (perp trading cheap) → funding negative → shorts pay longs.
  5. Either way, the economic incentive drags the perp back toward the spot index price (fed by an oracle).
  6. 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 pricePerShare climbs with interest.
  • Yearn popularized the pattern — a "yVault" is an ERC-4626 share whose totalAssets is 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?"