ERC-4626 Tokenized Vault Design: Share Math, Strategy Adapters, and Withdrawal Queues
How tokenized yield vaults are actually built: the share-price invariant, inflation attacks, strategy adapter interfaces, loss socialization, and why withdrawal queues exist.
Summary
ERC-4626 standardizes one idea: a vault where shares are a claim on a growing pool of a single underlying asset. Everything hard about building one lives in the conversion between assets and shares — rounding direction, the first-depositor inflation attack, how losses are socialized, and what happens when the strategy's capital is illiquid and a user wants out. The interface is trivial; the invariant is not.
The one invariant that matters
A vault is correct if and only if no sequence of deposits, withdrawals, and harvests lets any user extract more underlying than their proportional claim. Concretely: share price, defined as totalAssets divided by totalSupply, must never decrease as a result of a user action. It may increase from yield, and it may decrease from a reported strategy loss — but never because someone deposited or withdrew.
Write that as a Foundry invariant test before you write the vault. Every real bug in this class of contract shows up as a violation of it.
Rounding and the inflation attack
Conversions must always round against the user and toward the vault. Minting shares rounds down, burning to redeem rounds down on assets out, and previewing a withdrawal rounds up on shares required. Getting a single direction backwards creates a repeatable arbitrage that drains the vault one wei at a time.
function convertToShares(uint256 assets) public view returns (uint256) {
uint256 supply = totalSupply();
// Virtual offset: makes the first-depositor donation attack unprofitable
// by ensuring the share/asset ratio can never be inflated to 1:huge.
return supply == 0
? assets
: (assets * (supply + 10 ** DECIMALS_OFFSET)) / (totalAssets() + 1);
}The classic attack: an attacker deposits 1 wei, receives 1 share, then transfers 10,000 tokens directly to the vault address. Now one share is worth 10,000 tokens. The next depositor sends 15,000, and integer division mints them one share — the attacker withdraws and takes roughly half of the victim's deposit. Two independent mitigations exist, and I ship both: virtual shares and assets via a decimals offset, plus seeding the vault with a small dead deposit at deployment that can never be withdrawn.
Strategy adapters
Keep the vault dumb and the strategies replaceable. The vault knows how to hold the asset, mint shares, and route idle capital; it must not know that Aave exists. Each strategy implements a narrow interface, and the vault allocates across them by weight.
interface IStrategy {
function asset() external view returns (address);
function deposit(uint256 amount) external;
function withdraw(uint256 amount) external returns (uint256 actual);
function totalAssets() external view returns (uint256);
function harvest() external returns (int256 profitOrLoss);
function maxWithdraw() external view returns (uint256); // liquid right now
}Two details separate a toy from production. withdraw returns the actual amount recovered, because strategies routinely return less than requested under slippage — assuming success and crediting the request amount corrupts your accounting. And harvest returns a signed value: strategies lose money, and a vault that cannot represent a loss will simply revert forever when one occurs.
Loss socialization and profit locking
When a strategy reports a loss, someone eats it. Socializing it across all current holders — reducing share price immediately — is the honest default, but it creates a bank-run incentive: the first person out is unharmed. Report losses promptly and atomically with the harvest, so there is no window in which the stale higher share price is redeemable.
The mirror problem applies to profit. If a harvest credits 100,000 in yield in one transaction, a bot can deposit in the block before and withdraw in the block after, capturing yield it never earned. The fix is to unlock profit linearly over a period — typically several hours to a week — so that instantaneous share price never jumps.
Withdrawal queues: when instant redemption is a lie
ERC-4626's redeem() implies instant liquidity, and if your strategies hold anything illiquid, that promise is false. Rather than letting the vault revert unpredictably, make the constraint explicit: maxWithdraw reflects only currently liquid capital, and larger exits enter a queue that is serviced as strategies unwind.
- Keep an idle buffer — 5 to 20% of total assets — sized to cover ordinary daily outflow without touching strategies.
- Order strategy unwinding by a withdrawal queue that pulls from the most liquid, lowest-yield position first.
- Apply an exit fee that scales with how much of the buffer a withdrawal consumes, so that the cost of forcing an unwind lands on whoever forced it.
- Expose queue position and expected settlement time in the ABI — front ends and integrators need it, and users panic without it.
Frequently asked questions
- What is ERC-4626 in simple terms?
- A standard interface for vaults that take one ERC-20 token and issue shares representing a growing claim on a pool of it. Because every compliant vault exposes the same deposit, mint, withdraw, redeem, and preview functions, aggregators and front ends can integrate any vault without custom code.
- Is ERC-4626 safe to use out of the box?
- The OpenZeppelin implementation is a sound base and includes virtual-offset protection against inflation attacks, but the standard says nothing about strategies, losses, fees, or illiquidity. Those are exactly where vaults fail, and they are entirely your design responsibility.
- Can a vault hold multiple assets?
- Not under ERC-4626, which is single-asset by definition. Multi-asset vaults use ERC-7540 or a custom interface; a common pattern is a single-asset 4626 vault whose strategies internally hold diversified positions, so the standard interface still applies at the boundary.
Building something like this?
I'm Harsh Mittal — I build production systems across Web3, AI, and financial infrastructure: smart contracts and DeFi protocols, RAG pipelines and LLM agents, market data infrastructure, and the interfaces on top of them. If this is the kind of problem you're working on, I can help you ship it.