Anyone calls RevenueHandler.checkpoint to update the reward for the current epoch. checkpoint will calculate the reward based on the reward token balance of the contract. However, these balances include unclaimed rewards. In other words, the unclaimed rewards of some users are included in the rewards of the new epoch.
Vulnerability Details
Please look at the following code. When tokenConfig.poolAdapter is not set, thisBalance is directly used as the reward for the new epoch.
///// https://github.com/alchemix-finance/alchemix-v2-dao/blob/f1007439ad3a32e412468c4c42f62f676822dc1f/src/RevenueHandler.sol#L245-L264
uint256 thisBalance =IERC20(token).balanceOf(address(this));// If poolAdapter is set, the revenue token is an alchemic-tokenif (tokenConfig.poolAdapter !=address(0)) {// Treasury only receives revenue if the token is an alchemic-token treasuryAmt = (thisBalance * treasuryPct) / BPS;IERC20(token).safeTransfer(treasury, treasuryAmt);// Only melt if there is an alchemic-token to melt to amountReceived =_melt(token);// Update amount of alchemic-token revenue received for this epoch epochRevenues[currentEpoch][tokenConfig.debtToken] += amountReceived; } else {// If the revenue token doesn't have a poolAdapter, it is not an alchemic-token amountReceived = thisBalance;// Update amount of non-alchemic-token revenue received for this epoch epochRevenues[currentEpoch][token] += amountReceived; }
Suggested fix
Record the balance as a reward instead of using all the balance directly
Impact Details
Users who claim rewards every epoch will receive more rewards, and other users will receive less rewards