Users are able to claim more FLUX rewards than they should simply by merging their tokens. Since the merge() operation increases the _to token's balance, the _from token can simply increment its unclaimed FLUX through voter.reset() and then merge its balance into _to. Then _to can claim with the increased balance. This operation can continue for as many iterations as desired.
Vulnerability Details
Users increase their unclaimed FLUX balance through voter.reset() for their tokens after an epoch has passed. They can then claim their FLUX by calling FLUX.claimFlux().
However, since the claimableFlux() FLUX balance of each token is taken at the current block.timestamp, a user can simply call votingEscrow.merge() to merge their already claimed tokens with an unclaimed token, increasing its balance and effectively doubling their rewards to claim.
function claimableFlux(uint256 _tokenId) public view returns (uint256) {
// If the lock is expired, no flux is claimable at the current epoch
if (block.timestamp > locked[_tokenId].end) {
return 0;
}
// Amount of flux claimable is <fluxPerVeALCX> percent of the balance
return (_balanceOfTokenAt(_tokenId, block.timestamp) * fluxPerVeALCX) / BPS;
}