ERC4626 Inflation attack discussion

I was reading about vault inflation attacks from this article https://blog.openzeppelin.com/a-novel-defense-against-erc4626-inflation-attacks

Have doubt regarding third case mentioned(image attached)

It talks about dead shares(like burning of Minimum Liquidity in uni v2 pools during initial mint).

The cons mentions that "The strategy doesn't completely resolve the issue. Inflation attacks are still possible, at a reduced profit."

I want to know whether the attack is still profitable for the attacker since minimum liquidity is burned? I did some notebook math and can't find scenarios where donation attack is profitable but the article says otherwise. Does anyone have any more insights on this?

Hi, welcome to the community! :wave:

While this strategy doesn't completely resolve the issue, inflation attacks remain possible.
For instance, an attacker initially deposits 2 wei, receiving 1 wei as a share and leaving 1 wei as a dead share. Subsequently, the attacker can donate 200e18 tokens to the vault
before another user deposits 100e18 tokens. In this scenario, the user receives 0 shares
because shareAmountOut = assetsIn * totalSupply / totalAssets = 100e18 * 2 / (200e18 + 2) = 0. Consequently, 2 share tokens can acquire 300e18 assets (200e18 + 100e18), enabling attacker to obtain 150e18 assets by 1 share. Although this appears to result in a loss of 50e18 assets for the attacker, in reality, anyone depositing less than 100e18 assets will receive 0 shares, allowing the attacker to gain more assets per share.

2 Likes

Hey @Skyge , thanks for your reply. Won't such issues exist in the ERC4626 implementation by OpenZeppelin(https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC4626.sol) when function _decimalsOffset returns 0.

Do you mean the _decimalsOffset is equal to 0 at this function:

/**
     * @dev Internal conversion function (from assets to shares) with support for rounding direction.
     */
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
        return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
    }

    /**
     * @dev Internal conversion function (from shares to assets) with support for rounding direction.
     */
    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {
        return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);
    }
1 Like

Thanks @Skyge for pointing it out. Updated the comment.

You can have a look at the erc4626:defending_with_a_virtual_offset, which has a detailed explanation about this.

Thanks @Skyge for pointing it out. I had a look at that article.

The article mentions above points. It doesn't highlight the fact that attack can be profitable in same way as burning minimum liquidity of 1 shares when _decimalsOffset() function returns 0 in vault. The attacker will be at loss initially but when subsequent depositors gets 0 shares, it may turn into profits for attacker.

I think that would be helpful to add in that blog.

Emmm, not sure what do you mean, is it like this:

  • Attacker deposits 1 asset
  • Attacker donates some assets
  • Attacker burn 1 share
  • User deposits some assets

Or could you please explain the steps?

I meant something like following in ERC4626 vault where _decimalsOffset() returns 0. The attacker will be at loss initially but can turn it into profit eventually.

  • Attacker deposits 1 asset
  • Attacker shares =1
  • total shares = 2(due to virtual shares)
  • total asset = 2(due to virtual assets)

Victim wants to deposit 5000 but was frontrun:

Attacker donates = 10000

  • Attacker shares =1
  • total shares = 2(due to virtual shares)
  • total asset = 10002

Attacker loss due to virtual share of 1 = 10002/2 = 5001

  • Victim deposit = 5000
  • shares alloted = 2 * 5000/10002 = 0

Now,

  • Attacker shares =1
  • total shares = 2(due to virtual shares)
  • Victim shares = 0
  • total asset = 15002

Finally,
Attacker total deposit as of now = 15002
Attacker loss = 10000-15002/2 = 10000-7501 = 2499

With each deposits attacker will overcome loss and eventually get in profit. In this case if he had frontrun 3 victims with 5000 amount each then he would end up in profit

@Skyge have written the steps here: ERC4626 Inflation attack discussion - #9 by cryptomoon42

Not sure whether you are notified about the replies or not.

Notice your explanation, but need more time to confirm.

1 Like

Yes, you are right, have found something at the original PR about this, as it explained:

  • An offset of 0 doesn't offer protection
  • An offset of 9 makes attack very difficult, but changes the decimal value of the vault.
1 Like