There can be insufficient gauge rewards for users to claim.
When calculates gauage user's earned rewards, their staked amount will be boosted if they are holding veRAAC tokens.
And the user's rewards are calculated by the boosted amount.
User won't be able to claim rewards.
pragma solidity ^0.8.19;
import {Test, console, stdError} from "forge-std/Test.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "../contracts/libraries/math/WadRayMath.sol";
import "../contracts/core/pools/LendingPool/LendingPool.sol";
import "../contracts/core/pools/StabilityPool/StabilityPool.sol";
import "../contracts/mocks/core/tokens/crvUSDToken.sol";
import "../contracts/core/tokens/RToken.sol";
import "../contracts/core/tokens/DebtToken.sol";
import "../contracts/core/tokens/DeToken.sol";
import "../contracts/core/tokens/RAACToken.sol";
import "../contracts/core/tokens/RAACNFT.sol";
import "../contracts/core/tokens/veRAACToken.sol";
import "../contracts/core/primitives/RAACHousePrices.sol";
import "../contracts/core/minters/RAACMinter/RAACMinter.sol";
import "../contracts/core/collectors/FeeCollector.sol";
import "../contracts/core/collectors/Treasury.sol";
import "../contracts/core/governance/proposals/Governance.sol";
import "../contracts/core/governance/proposals/TimelockController.sol";
import "../contracts/core/governance/boost/BoostController.sol";
import "../contracts/core/governance/gauges/RAACGauge.sol";
import "../contracts/core/governance/gauges/GaugeController.sol";
import "../contracts/mocks/core/pools/MockPool.sol";
import "../contracts/mocks/core/tokens/MockToken.sol";
contract Audit is Test {
using WadRayMath for uint256;
using SafeCast for uint256;
address owner = makeAddr("Owner");
address repairFund = makeAddr("RepairFund");
LendingPool lendingPool;
StabilityPool stabilityPool;
RAACHousePrices raacHousePrices;
crvUSDToken crvUSD;
RToken rToken;
DebtToken debtToken;
DEToken deToken;
RAACToken raacToken;
RAACNFT raacNft;
veRAACToken veRaacToken;
RAACMinter raacMinter;
FeeCollector feeCollector;
Treasury treasury;
Governance governance;
TimelockController timelockController;
BoostController boostController;
GaugeController gaugeController;
RAACGauge raacGauge;
MockToken gaugeRewardToken;
MockToken gaugeStakingToken;
function setUp() public {
vm.warp(1740000000);
vm.startPrank(owner);
raacHousePrices = new RAACHousePrices(owner);
raacToken = new RAACToken(owner, 100, 50);
veRaacToken = new veRAACToken(address(raacToken));
crvUSD = new crvUSDToken(owner);
rToken = new RToken("RToken", "RToken", owner, address(crvUSD));
debtToken = new DebtToken("DebtToken", "DT", owner);
raacNft = new RAACNFT(address(crvUSD), address(raacHousePrices), owner);
deToken = new DEToken("DEToken", "DEToken", owner, address(rToken));
treasury = new Treasury(owner);
feeCollector = new FeeCollector(
address(raacToken),
address(veRaacToken),
address(treasury),
repairFund,
owner
);
lendingPool = new LendingPool(
address(crvUSD),
address(rToken),
address(debtToken),
address(raacNft),
address(raacHousePrices),
0.1e27
);
bytes memory data = abi.encodeWithSelector(
StabilityPool.initialize.selector,
address(rToken),
address(deToken),
address(raacToken),
address(owner),
address(crvUSD),
address(lendingPool)
);
address stabilityPoolProxy = address(
new TransparentUpgradeableProxy(
address(new StabilityPool(owner)),
owner,
data
)
);
stabilityPool = StabilityPool(stabilityPoolProxy);
raacMinter = new RAACMinter(
address(raacToken),
address(stabilityPool),
address(lendingPool),
owner
);
address[] memory proposers;
address[] memory executors;
timelockController = new TimelockController(2 days, proposers, executors, owner);
governance = new Governance(address(veRaacToken), address(timelockController));
boostController = new BoostController(address(veRaacToken));
gaugeController = new GaugeController(address(veRaacToken));
gaugeRewardToken = new MockToken("Reward Token", "RWD", 18);
gaugeStakingToken = new MockToken("veRAAC Token", "veRAAC", 18);
raacGauge = new RAACGauge(address(gaugeRewardToken), address(gaugeStakingToken), address(gaugeController));
raacHousePrices.setOracle(owner);
rToken.setReservePool(address(lendingPool));
debtToken.setReservePool(address(lendingPool));
deToken.setStabilityPool(address(stabilityPool));
stabilityPool.setRAACMinter(address(raacMinter));
lendingPool.setStabilityPool(address(stabilityPool));
raacToken.setMinter(address(raacMinter));
raacToken.setFeeCollector(address(feeCollector));
raacToken.manageWhitelist(address(feeCollector), true);
raacToken.manageWhitelist(address(raacMinter), true);
raacToken.manageWhitelist(address(stabilityPool), true);
raacToken.manageWhitelist(address(veRaacToken), true);
timelockController.grantRole(keccak256("PROPOSER_ROLE"), address(governance));
timelockController.grantRole(keccak256("EXECUTOR_ROLE"), address(governance));
timelockController.grantRole(keccak256("CANCELLER_ROLE"), address(governance));
timelockController.grantRole(keccak256("EMERGENCY_ROLE"), address(governance));
vm.stopPrank();
vm.label(address(crvUSD), "crvUSD");
vm.label(address(rToken), "RToken");
vm.label(address(debtToken), "DebtToken");
vm.label(address(deToken), "DEToken");
vm.label(address(raacToken), "RAACToken");
vm.label(address(raacNft), "RAAC NFT");
vm.label(address(lendingPool), "LendingPool");
vm.label(address(stabilityPool), "StabilityPool");
vm.label(address(raacMinter), "RAACMinter");
vm.label(address(veRaacToken), "veRAAC");
vm.label(address(feeCollector), "FeeCollector");
vm.label(address(treasury), "Treasury");
vm.label(address(governance), "Governance");
vm.label(address(timelockController), "TimelockController");
vm.label(address(boostController), "BoostController");
vm.label(address(gaugeController), "GaugeController");
vm.label(address(raacGauge), "RAACGauge");
vm.label(address(gaugeRewardToken), "GaugeRewardToken");
vm.label(address(gaugeStakingToken), "GaugeStakingToken");
}
function testAudit_GaugeWeightBoost() public {
vm.prank(owner);
gaugeController.addGauge(address(raacGauge), IGaugeController.GaugeType.RAAC, 10000);
(,,,uint256 periodStartTime) = raacGauge.periodState();
vm.warp(periodStartTime);
address alice = makeAddr("Alice");
address bob = makeAddr("Bob");
deal(address(raacToken), alice, 100e18);
vm.startPrank(alice);
raacToken.approve(address(veRaacToken), 100e18);
veRaacToken.lock(100e18, 1460 days);
vm.stopPrank();
deal(address(raacToken), bob, 100e18);
vm.startPrank(bob);
raacToken.approve(address(veRaacToken), 100e18);
veRaacToken.lock(100e18, 1460 days);
vm.stopPrank();
uint256 stakingAmount = 100e18;
deal(address(gaugeStakingToken), alice, stakingAmount);
deal(address(gaugeStakingToken), bob, stakingAmount);
vm.startPrank(alice);
gaugeStakingToken.approve(address(raacGauge), stakingAmount);
raacGauge.stake(stakingAmount);
vm.stopPrank();
vm.startPrank(bob);
gaugeStakingToken.approve(address(raacGauge), stakingAmount);
raacGauge.stake(stakingAmount);
vm.stopPrank();
deal(address(gaugeRewardToken), address(raacGauge), 10000e18);
vm.prank(address(gaugeController));
raacGauge.notifyRewardAmount(10000e18);
vm.warp(block.timestamp + raacGauge.getPeriodDuration());
vm.prank(alice);
raacGauge.getReward();
vm.prank(bob);
vm.expectRevert(IGauge.InsufficientBalance.selector);
raacGauge.getReward();
}
}
The fixing is not trivial, protocol should be aware of each staker's boosted amount, and distribute rewars on a pro rata basis.