Liquidations inflate the share decimals to dilute existing holders. Over successive liquidations, this exponential share inflation can trigger arithmetic overflows during withdrawal calculations, locking funds and potentially causing significant losses for users.
When shares are first minted to depositors, they use 14 decimals (6 decimals for USDC * 1e8):
However, the number of decimals can increase when a liquidation occurs.
Let's say there are 100,000e14 shares and there's an open 2x leverage long position. A user deposits 1000e6 USDC to the position, but before runNextAction() is called by a keeper, a liquidation occurs liquidating the entire position.
Gamma doesn't burn the 100,000e14 shares, but instead dilutes them to them point where they are essentially worthless. This is done in the shares calculation.
First, we calculate the totalAmountBefore, which is the total value of the vault prior to the deposit. Since the position is liquidated this will be 0.
Then we multiple the amount deposited by the total number of shares and then divide by totalAmountBefore to determine how many shares to mint.
To avoid a divide by 0 error, Gamma updates 0 to 1:
Now, when we mint the depositor their shares, they receive 1000e6 * 100,000e14 / 1 shares = 1000e25.
Since the prior 100,000e14 shareholders position was liquidated, their shares are only now worth ~.0000001% of the position.
This seems to work, but a big problem arrises the more the vault gets liquidated.
Let's say the next time a liquidation occurs, the vault has 100,000e25 shares. The next depositor would receive 1000e6 * 100,000e25 = 1000e36 shares
Since shares are a uint256, the maximum value is 2^256 - 1 or approximately ~1.157e77.
It's reasonable to assume that each time a liquidation occurs, the decimals on shares get scaled up somewhere between 9 and 11 decimal places, which means that after ~6 liquidations, we'd be approaching the max value of a uint256.
This poses huge problems when a user attempts to withdraw their funds. When \_withdraw is called there are multiple places (denoted below) where an overflow could occur because shares are multiplied by something:
The overflow would lead to funds becoming stuck for withdrawers.
It's important to point out that this overflow can occur if there's less than 6 liquidations depending on how large the value shares is being multiplied against. For example, sizeDeltaInUSD uses 30 decimals, so if shares approach 77-30 = 47 decimals, this overflow is a risk and would only take ~3 liquidations.
Loss of funds.
Manual review.
Consider potentially burning the shares of users when a position is liquidated so decimals don't scale.
Likelihood: Low/Medium, when fully liquidated. Liquidation often returns some tokens and shares are important to withdraw them. Moreover, shares are inflated, so only little part of tokens with huge value (WBTC) will be impacted. Impact: High, Previous depositor is able to withdraw token from the new depositors if the value of the token is huge like for WBTC.
Likelihood: Extremely low, full liquidation several times, and the admin should unpause several times the vault. But since keeper is the real component to check during a liquidation, it's still valid. Impact: High, any new deposit once the above conditions are met, will inflates shares and DoS the vault.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.