Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Invalid

Dos in guage contract due to overflow

Summary

Staking, Withdrawing or any other function that implements updateReward modifier will be broken and revert due to overflow.
This means the whole Guage system is bricked

Vulnerability Details

function _applyBoost(address account, uint256 baseWeight) internal view virtual returns (uint256) {
if (baseWeight == 0) return 0;
IERC20 veToken = IERC20(IGaugeController(controller).veRAACToken());
uint256 veBalance = veToken.balanceOf(account);
uint256 totalVeSupply = veToken.totalSupply();
// Create BoostParameters struct from boostState
BoostCalculator.BoostParameters memory params = BoostCalculator.BoostParameters({
maxBoost: boostState.maxBoost,
minBoost: boostState.minBoost,
boostWindow: boostState.boostWindow,
totalWeight: boostState.totalWeight,
totalVotingPower: boostState.totalVotingPower,
votingPower: boostState.votingPower
});
uint256 boost = BoostCalculator.calculateBoost(
veBalance,
totalVeSupply,
params
);
@>> return baseWeight * boost /1e8 ;
}

The function above is an internal function found in the unpdateReward modifier and applies boost multiplier to base weight of the user, however, due to how the boost is calculated, it will overflow causing all functions that have implemented the modifier to revert.

Proof Of Concept

See how to integrate foundry to hardhat project
. Create a new file POC.t.sol in project /test/ folder . Paste the poc and run forge test --mt test_POC

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "contracts/core/governance/gauges/GaugeController.sol";
import "contracts/core/governance/gauges/RWAGauge.sol";
import "contracts/core/governance/gauges/RAACGauge.sol";
import "contracts/mocks/core/tokens/MockToken.sol";
contract GaugeControllerTest is Test {
event RewardDistributed(address indexed gauge, address indexed user, uint256 amount);
GaugeController public gaugeController;
RWAGauge public rwaGauge;
RAACGauge public raacGauge;
MockToken public stakingToken;
MockToken public rewardToken;
MockToken public veRAACToken;
address owner = address(1);
address gaugeAdmin = address(2);
address emergencyAdmin = address(3);
address feeAdmin = address(4);
address user1 = address(5);
address user2 = address(6);
uint256 constant MONTH = 30 days;
uint256 constant WEEK = 7 days;
uint256 constant WEIGHT_PRECISION = 10000;
function setUp() public {
// Deploy tokens
veRAACToken = new MockToken("veRAAC Token", "veRAAC", 18);
rewardToken = new MockToken("Reward Token", "REWARD", 18);
stakingToken = new MockToken("Reward Token", "REWARD", 18);
// Deploy GaugeController
gaugeController = new GaugeController(address(veRAACToken));
// Deploy Gauges
rwaGauge = new RWAGauge(
address(rewardToken),
address(stakingToken),
address(gaugeController)
);
raacGauge = new RAACGauge(
address(rewardToken),
address(stakingToken),
address(gaugeController)
);
// Setup roles
bytes32 GAUGE_ADMIN_ROLE = gaugeController.GAUGE_ADMIN();
bytes32 EMERGENCY_ADMIN_ROLE = gaugeController.EMERGENCY_ADMIN();
bytes32 FEE_ADMIN_ROLE = gaugeController.FEE_ADMIN();
gaugeController.grantRole(GAUGE_ADMIN_ROLE, gaugeAdmin);
gaugeController.grantRole(EMERGENCY_ADMIN_ROLE, emergencyAdmin);
gaugeController.grantRole(FEE_ADMIN_ROLE, feeAdmin);
// Add gauges
vm.prank(gaugeAdmin);
gaugeController.addGauge(address(rwaGauge), IGaugeController.GaugeType.RWA, 0); // RWA type
vm.prank(gaugeAdmin);
gaugeController.addGauge(address(raacGauge), IGaugeController.GaugeType.RAAC, 0); // RAAC type
// Initialize gauges
rwaGauge.grantRole(rwaGauge.CONTROLLER_ROLE(), owner);
raacGauge.grantRole(raacGauge.CONTROLLER_ROLE(), owner);
}
function test_POC() public {
// Setup initial conditions
uint256 amountTostake = 1 ether;
stakingToken.mint(user1, amountTostake);
veRAACToken.mint(user1, 1 ether);
rewardToken.mint(address(rwaGauge), 1000000 ether);
vm.prank(user1);
gaugeController.vote(address(rwaGauge), 5000); // 50% weight
// Fast forward to align with period
vm.warp(((block.timestamp / MONTH) + 1) * MONTH);
vm.startPrank(user1);
stakingToken.approve(address(rwaGauge), amountTostake);
// user cannot stake
vm.expectRevert();
rwaGauge.stake(amountTostake);
// overlfow
vm.expectRevert();
rwaGauge.getReward();
vm.stopPrank();
}
}

Impact

The whole Guage system is bricked, That is, users will not be able to deposit or withdraw their funds.

Tools Used

Manual Review

Recommendations

Handle the boost calculations correctly

Updates

Lead Judging Commences

inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.