Reentrancy Attack Vectors in ERC-4626 Tokenized Vaults
The ERC-4626 standard specifies yield-bearing vaults. While standardizing deposits, withdrawals, and share conversions simplifies integration, it introduces specific security risks: read-only reentrancy and share price inflation.
1. Read-Only Reentrancy in ERC-4626 Integrations
A read-only reentrancy vulnerability occurs when an external contract queries a vault's exchange rate while the vault is in a transient, inconsistent state. Although the view functions do not modify the vault's state directly, the returned rate is inaccurate.
Attack Scenario:
- An attacker withdraws a massive amount of assets from a liquidity pool vault.
- The pool contract sends assets to the attacker, but has not yet updated its internal total assets tracking.
- During the transfer, the attacker triggers a callback that queries the vault's
convertToAssets(shares)function. - The vault returns a inflated exchange rate because
totalAssets()is still reporting the old balance. - The attacker uses this stale pricing to exploit a third-party lending protocol that relies on the vault share price as oracle collateral.
2. Share Price Inflation (First-Depositor Attack)
An attacker can exploit rounding errors in vault share calculations when the vault's total supply is zero.
The Math:
Let assets be the deposit size, totalAssets() the vault's asset balance, and totalSupply() the outstanding shares.
Step-by-Step Attack:
- The attacker deposits
1 weiof underlying asset, receiving1 weiof shares. - The attacker transfers
10,000 ETHdirectly to the vault contract (using a direct transfer, notdeposit()). - Now,
totalAssets() = 10,000 ETH + 1 wei, whiletotalSupply() = 1 wei. - A victim deposits
5,000 ETH. - The share calculation is:
- The victim receives 0 shares but the vault keeps their 5,000 ETH, which the attacker redeems using their 1 wei share.
3. Remediation Patterns
A. Virtual Shares (Offset Method)
Ensure calculations include a virtual buffer (shares offset) to prevent inflation:
function _convertToShares(uint256 assets) internal view returns (uint256) {
// Adds a virtual 10^3 offset to assets and shares
return assets * (totalSupply() + 10**3) / (totalAssets() + 10**3);
}
B. Non-Reentrant view functions
Use reentrancy guards on view functions to prevent reading transient states:
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract SecureVault is ReentrancyGuard {
// ...
function convertToAssets(uint256 shares) public view returns (uint256) {
// Rejects execution if a state-modifying function is currently active
require(!_reentrancyGuardEntered(), "Reentrancy guard active");
return _convertToAssets(shares);
}
}
C. Checks-Effects-Interactions for accounting updates
Vaults that send assets before updating total accounting create a window where an external observer can see a pre-withdrawal asset base. The safer pattern is to burn shares, update internal debt or accounting caches, then perform the external transfer. If the vault relies on asset.balanceOf(address(this)) as the sole source of truth, pair it with a cached accounting variable for operations that must remain consistent during the transaction.
function withdraw(uint256 assets, address receiver, address owner) public nonReentrant returns (uint256 shares) {
shares = previewWithdraw(assets);
if (msg.sender != owner) _spendAllowance(owner, msg.sender, shares);
_burn(owner, shares);
accountedAssets -= assets;
SafeERC20.safeTransfer(IERC20(asset()), receiver, assets);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
}
D. Minimum liquidity and donation-resistant accounting
Virtual shares reduce the first-depositor attack, but production vaults should also define a minimum initial deposit and decide whether unsolicited asset transfers are recognized. A conservative vault treats direct donations as protocol surplus until explicitly swept or accounted through a controlled function.
function totalAssets() public view override returns (uint256) {
return accountedAssets;
}
function syncDonations(uint256 maxAssets) external onlyRole(ACCOUNTANT_ROLE) {
uint256 surplus = IERC20(asset()).balanceOf(address(this)) - accountedAssets;
require(surplus <= maxAssets, "unexpected donation");
accountedAssets += surplus;
}
4. Integration Risks for Lending Protocols
ERC-4626 shares are often accepted as collateral. A lending market that reads convertToAssets() directly during borrow or liquidation inherits the vault's transient-state risk. The safest integrations use a delayed oracle: observe the vault exchange rate over time, bound per-block movement, and reject rates that move faster than the underlying strategy could plausibly earn yield.
Key controls:
- Rate clamps: cap share-price movement per block or per hour.
- TWAP share pricing: smooth
totalAssets / totalSupplyover a window longer than one transaction. - Asset-liability reconciliation: compare vault-reported assets against strategy balances and pending withdrawals.
- Circuit breakers: disable new borrows when share price moves outside expected bounds.
5. Auditor Checklist
-
deposit,mint,withdraw, andredeemfollow checks-effects-interactions. -
convertToAssetsandconvertToSharescannot return inflated values during an active withdrawal. - First-depositor behavior is protected by virtual shares, minimum liquidity, or dead shares.
- Direct asset donations cannot force later depositors to mint zero shares.
- Rounding direction matches ERC-4626 expectations for each preview function.
- Integrators do not use raw vault exchange rates as immediate collateral or liquidation oracle values.
- All fee-on-transfer or rebasing asset assumptions are explicitly rejected or handled.
6. Conclusion
ERC-4626 reduces integration ambiguity, but it does not remove vault-specific security work. The core question for auditors is not simply whether the standard methods exist, but whether the share price remains economically honest across callbacks, donations, rounding boundaries, and downstream collateral usage. A vault is only safe when both its internal accounting and its integrators can tolerate adversarial transaction ordering.