Defense on inflation attack ERC4626

In the doc of https://docs.openzeppelin.com/contracts/5.x/erc4626#defending_with_a_virtual_offset, it describes that
image
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.

  1. Initially, totalAssets and totalShares are 0

  2. The attacker deposits 1 token, gets the shareReceived = 1 * 1 / 1 = 1 (totalShares = 1 )

  3. User plan to deposit 1000 tokens

  4. 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

  1. User deposit 1000 tokens, get the shareReceived = 1000 * (1 + 1) / 2001 = 0, vault state:
    totalAssets = 3000, totalShares = 1
  2. Attack redeem 1 share, receive token amount = 1 * (3001) / (1 + 1 ) = 1500
1 Like

Hi, welcome to the community! :wave:

I have noticed this, and opened a relative issue on github: https://github.com/OpenZeppelin/openzeppelin-contracts/issues/5223