Core Contracts

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

Boost state values are initialized incorrectly in BaseGauge contract

Summary

Boost state parameters are initialized incorrectly in constructor of BaseGauge contract

Vulnerability Details

In BaseGauge constructor, boost state are initialized as boostState.maxBoost = 25000 and boostState.minBoost = 1e18. So boostState.minBoost > boostState.maxBoost right after contract deployment. This can cause users unable to earn rewards because the function BaseGauge::_applyBoost() reverts due to arithmetic underflow when it calls library function BoostCalculator::calculateBoost().

// BaseGauge
constructor(
address _rewardToken,
address _stakingToken,
address _controller,
uint256 _maxEmission,
uint256 _periodDuration
) {
rewardToken = IERC20(_rewardToken);
stakingToken = IERC20(_stakingToken);
controller = _controller;
// Initialize roles
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(CONTROLLER_ROLE, _controller);
// Initialize boost parameters
@> boostState.maxBoost = 25000; // 2.5x
@> boostState.minBoost = 1e18; // <<--- minBoost > maxBoost here
boostState.boostWindow = 7 days;
uint256 currentTime = block.timestamp;
uint256 nextPeriod = ((currentTime / _periodDuration) * _periodDuration) + _periodDuration;
// Initialize period state
periodState.periodStartTime = nextPeriod;
periodState.emission = _maxEmission;
TimeWeightedAverage.createPeriod(
periodState.votingPeriod,
nextPeriod,
_periodDuration,
0,
10000 // VOTE_PRECISION
);
}
function earned(address account) public view returns (uint256) {
@> return (getUserWeight(account) *
(getRewardPerToken() - userStates[account].rewardPerTokenPaid) / 1e18
) + userStates[account].rewards;
}
function getUserWeight(address account) public view virtual returns (uint256) {
uint256 baseWeight = _getBaseWeight(account);
@> return _applyBoost(account, baseWeight);
}
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) / 1e18;
}
// BoostCalculator
function calculateBoost(
uint256 veBalance,
uint256 totalVeSupply,
BoostParameters memory params
) internal pure returns (uint256) {
// Return base boost (1x = 10000 basis points) if no voting power
if (totalVeSupply == 0) {
return params.minBoost;
}
// Calculate voting power ratio with higher precision
uint256 votingPowerRatio = (veBalance * 1e18) / totalVeSupply;
// Calculate boost within min-max range
@> uint256 boostRange = params.maxBoost - params.minBoost; // <<<== underflow here
uint256 boost = params.minBoost + ((votingPowerRatio * boostRange) / 1e18);
// Ensure boost is within bounds
if (boost < params.minBoost) {
return params.minBoost;
}
if (boost > params.maxBoost) {
return params.maxBoost;
}
return boost;
}

PoC

The console shows

BaseGauge
Weight Management
1) should calculate user weight with boost
0 passing (2s)
1 failing
1) BaseGauge
Weight Management
should calculate user weight with boost:
Error: VM Exception while processing transaction: reverted with panic code 0x11 (Arithmetic operation overflowed outside of an unchecked block)
at MockBaseGauge.calculateBoost (contracts/libraries/governance/BoostCalculator.sol:89)
at MockBaseGauge._applyBoost (contracts/core/governance/gauges/BaseGauge.sol:247)
at MockBaseGauge.getUserWeight (contracts/core/governance/gauges/BaseGauge.sol:642)

Impact

  • Users unable to update and earn rewards

  • Functions stake(), withdraw(), getReward(), voteDirection(), checkpoint() are DoS right after deployment

Tools Used

Manual

Recommendations

boostState.maxBoost = 25000; // 2.5x
- boostState.minBoost = 1e18;
+ boostState.minBoost = 10000;
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

boostState.minBoost is set to 1e18

Support

FAQs

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