In the doc of https://docs.openzeppelin.com/contracts/5.x/erc4626#defending_with_a_virtual_offset, it describes that
But in the implementation
function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
}
If the _decimalOffset is zero, the attacker loss is half of user's deposit instead of deposit. Why does the implementation is different from the doc?
Here is the example.
-
Initially, totalAssets and totalShares are 0
-
The attacker deposits 1 token, gets the shareReceived = 1 * 1 / 1 = 1 (totalShares = 1 )
-
User plan to deposit 1000 tokens
-
For making user receive 0 share, attacker need to transfer x tokens before user
Thus, the minimum x should be 1999. The total assets of the vault is 2000
- User deposit 1000 tokens, get the shareReceived = 1000 * (1 + 1) / 2001 = 0, vault state:
totalAssets = 3000, totalShares = 1 - Attack redeem 1 share, receive token amount = 1 * (3001) / (1 + 1 ) = 1500