Vault Rounding Remediation
Overview
Related Detector: Vault Rounding
Tokenized vaults (ERC-4626) convert between assets and shares with integer division, so rounding direction decides who absorbs the truncated remainder. If conversions round in the user’s favour, a depositor can extract value; and an empty vault is vulnerable to the classic inflation attack, where the first depositor mints one wei-share, donates assets directly to the vault to inflate the share price, and makes later deposits round down to zero shares. Remediation combines correct rounding direction with an inflation defence.
Recommended Fix
Before (Vulnerable)
// Rounds shares up on deposit and assets up on withdraw — both favour the user.
// Empty-vault share price is unprotected, enabling the inflation attack.
function deposit(uint256 assets) external returns (uint256 shares) {
shares = totalSupply == 0 ? assets : (assets * totalSupply + totalAssets - 1) / totalAssets;
_mint(msg.sender, shares);
}
After (Fixed — round against the user, add virtual shares)
// Round shares DOWN on deposit / mint fewer; round assets DOWN on redeem.
// Virtual shares and assets (a constant offset) make the empty-vault price
// impossible to inflate to a meaningful ratio.
uint256 private constant VIRTUAL = 1e3;
function convertToShares(uint256 assets) public view returns (uint256) {
return (assets * (totalSupply + VIRTUAL)) / (totalAssets + 1); // floor division
}
The most robust option is to inherit a reviewed ERC-4626 implementation (OpenZeppelin’s uses the virtual-shares approach) rather than hand-rolling the conversion math. Optionally seed the vault with a small first deposit at deployment so it is never empty.
Common Mistakes
- Rounding both
depositandwithdrawin the user’s favour, so every round trip leaks a wei to the caller. - Adding a minimum-deposit check but leaving the empty-vault share price inflatable.
- Copying
mulDivwithRounding.UpwhereRounding.Downis required for the protocol-favouring direction.