✅Community Workshop: Riley Holterhus
Last updated
Last updated
This workshop covers ERC4626 inflation attack.
Video:
Slides:
This is the inflation attack I mentioned.
The contract implements basic ERC4626 operations. Users can deposit WETH to get vault shares, and they can burn vault shares to get WETH back, plus interest.
The bug is in the deposit()
function:
The sharesToMint
math can go wrong. Recall that integer division in Solidity rounds down. This rounding issue leads to inflation attack against the first victim depositor.
Here is a table summarizing the attacking steps:
Basically, attacker deposits 1 wei to mint 1 share in a newly-created vault.
Then, the attacker transfers 100 WETH directly to the contract without using the deposit()
function. This step is done by transfer()
or transferFrom()
. The attacker is losing money here to inflate the vault. Now the totalSupply
is still 1 share but balanceOf
becomes a huge number 1 + 100 * 10**18.
When the first victim depositor deposits, say, 200 WETH, the computation for sharesToMint
goes wrong. The formula is:
Substitute in:
We see the victim lost 0.9999... share in this deposit. Now attacker owns 1 share and victim owns 1 share, so they evenly own all the money in the vault.
In the end, attacker withdraws 1 share. How much can the attacker profit:
LP tokens instead of vault shares
Manually supply liquidity instead of manual transfer of tokens
Pretty similar, and you can see a first deposit of $60M USD 🤯
The solution mentioned in the workshop is minting some of the initial amount to the zero address. This makes the "rounded down" shares less dominate in the computation.
I would suggest using a "multiplier" just like what is used for computing floating point numbers. Suppose the multiplier is 6, then 1.9999.. shares -> 1999999.999... shares. The "rounded down" part becomes less important.