ERC4626 Calculation Bugs
id: LS34H
title: ERC4626 Calculation Bugs
baseSeverity: H
category: accounting
language: solidity
blockchain: [ethereum]
impact: Mispriced shares, loss of user funds, or broken vault logic
status: draft
complexity: medium
attack_vector: internal
mitigation_difficulty: medium
versions: [">=0.8.0", "<=0.8.25"]
cwe: CWE-682
swc: SWC-135
📝 Description
- ERC-4626 is a standardized interface for yield-bearing vaults that tokenizes user deposits into shares. Implementing this standard incorrectly—particularly the conversion math between assets and shares—can lead to:
- Rounding errors that compound over time
- Unfair share issuance (underpricing or overpricing)
- Incorrect redemption behavior (withdrawals exceeding vault assets)
- Economic imbalances between early and late users
- Common causes of bugs include:
- Integer division without rounding control
- Unchecked edge cases when totalSupply == 0
- Asset/share misalignment during fee handling or rebasing
🚨 Vulnerable Code
pragma solidity ^0.8.0;
contract BrokenVault {
uint256 public totalAssets;
uint256 public totalShares;
function convertToShares(uint256 assets) public view returns (uint256) {
return (assets * totalShares) / totalAssets; // ❌ division by 0 if empty vault
}
function convertToAssets(uint256 shares) public view returns (uint256) {
return (shares * totalAssets) / totalShares;
}
}
🧪 Exploit Scenario
- Vault is freshly deployed with totalAssets == 0 and totalShares == 0.
- First user deposits 100 tokens.
- The vault incorrectly mints zero shares due to division rounding to zero.
- Subsequent users get full-value shares, and the initial depositor cannot withdraw.
- Alternatively, early users may get all shares, diluting future users unfairly.
Assumptions:
- Vault does not implement ERC4626’s initialization safety check for zero supply.
- No precision-aware rounding or safety guards are in place.
✅ Fixed Code
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
contract SafeVault is ERC4626 {
constructor(IERC20 asset_) ERC4626(asset_) {}
function convertToShares(uint256 assets) public view override returns (uint256) {
uint256 _supply = totalSupply();
return (_supply == 0) ? assets : (assets * _supply) / totalAssets();
}
function convertToAssets(uint256 shares) public view override returns (uint256) {
uint256 _supply = totalSupply();
return (_supply == 0) ? shares : (shares * totalAssets()) / _supply;
}
}
🧭 Contextual Severity
- context: "Vault governs user deposits and redemptions without input validation"
severity: H
reasoning: "User funds may be trapped or extracted unfairly"
- context: "Vault uses OpenZeppelin 4626 with basic checks"
severity: M
reasoning: "Still prone to rounding and edge case bugs"
- context: "Vault includes floor liquidity, rounding protection, and unit tests"
severity: L
reasoning: "Risk minimized if fully hardened"
🛡️ Prevention
Primary Defenses
- Always guard against totalAssets == 0 and totalShares == 0.
- Use OpenZeppelin’s ERC4626 reference implementation unless there is a strong reason not to.
Additional Safeguards
- Write fuzz tests for share/asset conversion functions (convertToAssets, convertToShares, previewDeposit, previewRedeem)
- Validate initial minting behavior with property-based testing (e.g., Foundry, Echidna).
Detection Methods
- Search for convertToShares and convertToAssets implementations without fallback logic.
- Tools: Slither, MythX, invariant testing
🕰️ Historical Exploits
- Name: Astaria ERC4626 Share Inflation Exploit
- Date: 2023-01
- Loss: Potential for significant user losses due to manipulated share price calculations
- Post-mortem: Link to post-mortem
- Name: OpenZeppelin ERC4626 Inflation Attack Discussion
- Date: 2022-09
- Loss: Identified vulnerabilities in early ERC4626 implementations leading to potential user losses
- Post-mortem: Link to post-mortem
📚 Further Reading
✅ Vulnerability Report
id: LS34H
title: ERC4626 Calculation Bugs
severity: H
score:
impact: 4
exploitability: 3
reachability: 4
complexity: 2
detectability: 4
finalScore: 3.55
📄 Justifications & Analysis
- Impact: Loss of user funds, broken accounting, unfair share ratios.
- Exploitability: Can be triggered by any depositor if vault isn't protected.
- Reachability: Applies to all vaults implementing custom ERC4626 logic.
- Complexity: Not complex but easy to overlook.
- Detectability: Detected via fuzzing, property testing, or static checks.