3. Boost is calculated without proper scaling to user's token holdings
pragma solidity ^0.8.19;
import {Test} from "forge-std/Test.sol";
import {BoostController} from "../../../../../contracts/core/governance/boost/BoostController.sol";
import {veRAACToken} from "../../../../../contracts/core/tokens/veRAACToken.sol";
import {RAACToken} from "../../../../../contracts/core/tokens/RAACToken.sol";
contract BoostControllerHardcodedBaseTest is Test {
BoostController public boostController;
veRAACToken public veToken;
RAACToken public raacToken;
address public attacker = address(0x1);
address public legitUser = address(0x2);
address public pool = address(0x3);
address public owner = address(this);
function setUp() public {
raacToken = new RAACToken(owner, 100, 50);
veToken = new veRAACToken(address(raacToken));
boostController = new BoostController(address(veToken));
boostController.modifySupportedPool(pool, true);
raacToken.setMinter(owner);
raacToken.mint(attacker, 100e18);
raacToken.mint(legitUser, 10000e18);
raacToken.manageWhitelist(attacker, true);
raacToken.manageWhitelist(legitUser, true);
raacToken.manageWhitelist(address(veToken), true);
}
function testHardcodedBaseVulnerability() public {
vm.startPrank(legitUser);
raacToken.approve(address(veToken), type(uint256).max);
veToken.lock(10000e18, 365 days);
vm.stopPrank();
vm.startPrank(attacker);
raacToken.approve(address(veToken), type(uint256).max);
veToken.lock(100e18, 365 days);
vm.stopPrank();
vm.prank(legitUser);
boostController.updateUserBoost(legitUser, pool);
(uint256 legitUserBoost, , , ) = boostController.getUserBoost(legitUser, pool);
vm.prank(attacker);
boostController.updateUserBoost(attacker, pool);
(uint256 attackerBoost, , , ) = boostController.getUserBoost(attacker, pool);
uint256 attackerBoostPerToken = (attackerBoost * 1e18) / veToken.balanceOf(attacker);
uint256 legitUserBoostPerToken = (legitUserBoost * 1e18) / veToken.balanceOf(legitUser);
assertGt(
attackerBoostPerToken,
legitUserBoostPerToken,
"Attacker gets more boost per token"
);
uint256 boostRatio = (attackerBoost * 100) / legitUserBoost;
uint256 tokenRatio = (veToken.balanceOf(attacker) * 100) / veToken.balanceOf(legitUser);
assertGt(
boostRatio,
tokenRatio * 20,
"Attacker gets disproportionate boost relative to holdings"
);
uint256 dailyRewards = 1000e18;
uint256 totalBoost = attackerBoost + legitUserBoost;
uint256 attackerRewards = (attackerBoost * dailyRewards) / totalBoost;
uint256 legitUserRewards = (legitUserBoost * dailyRewards) / totalBoost;
uint256 attackerRewardEfficiency = (attackerRewards * 1e18) / veToken.balanceOf(attacker);
uint256 legitUserRewardEfficiency = (legitUserRewards * 1e18) / veToken.balanceOf(legitUser);
assertGt(
attackerRewardEfficiency,
legitUserRewardEfficiency,
"Attacker gets more rewards per token held"
);
assertGt(
attackerBoost,
10000,
"Even small holder gets more than base amount"
);
}
}
Use the userbalance instead of using an hardcoded amount .