Gauges will become insolvent due to a faulty check
notifyRewardAmount
is used by the gauge controller to allocate rewards for gauges, where inside each gauge 2 very important checks are made.
First is if amount > periodState.emission
to make sure we are not breaching the max emissions.
Second is if we have enough balance - rewardToken.balanceOf(address(this))
The issue here is that the second check does not take into account users who have not yet claimed their rewards. These users will have their rewards inside this contract, contributing to a larger balance. And if notifyRewardAmount
is called with an amount larger than the actual available balance then more rewards will be distributed that there are inside this contract, which would lead to insolvency as some users won't be able to claim.
This will likely happen with _distributeToGauges
as it does not take into account any of those checks and distributes based on gauge weight. Where if our gauge has really big weight (is overweight :D) it would receive a larger portion of the distributed amount.
Same can be said for if we use distributeRewards
, as it can too distribute more rewards than the current available balance.
There are 10k tokens inside the gauge, where 6k are left to be distributed (just sitting there) and 4k are already distributed, just users haven't claimed them yet
Users call distributeRewards
and since this gauge is heavier than the rest it gets allocated 8k rewards to distribute (there are not sent, just the gauge is configured to distribute them)
notifyRewardAmount
passes as the gauge has 10k balance (more than enough to cover the to-be distributed 8k)
Our gauge allocates 8k to be distributed and when the cycle finishes, there would be 12k rewards available to be claimed, but only 10k tokens inside the gauge. Our gauge is insolvent.
Gauge is insolvent
Some users receive more rewards while the last to claim are unable to since there are not enough tokens inside the contract.
Manual review
Add 2 variables - totalClaimed
rewards and totalDistributed
rewards, where in order to calculate how much are distributed, but not yet claimed just do totalDistributed - totalClaimed
, then to find how much real balance the gauge has - rewardToken.balanceOf(address(this)) - (totalDistributed - totalClaimed)
.
And of course update those 2 variables whenever necessary.
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.