Core Contracts

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

hardcoded baseamount in Updateuserboost fucntion causes users with small token holdings to receive higher boosts relative to their holdings t

Summary

In the BoostController.sol contract there is a hardcoded base amount (10000) in the updateUserBoost() function which leads to disproportionate boost allocations. This allows users with small token holdings to receive significantly higher boosts relative to their holdings than larger token holders, breaking the intended boost distribution mechanism.

Vulnerability Details

Vulnerable Code: https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/boost/BoostController.sol#L177

function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
// ...
uint256 newBoost = _calculateBoost(user, pool, 10000); //audit: Hardcoded base amount
// ...
}

Execution Flow:

  • User calls updateUserBoost()

  • Function calls _calculateBoost() with hardcoded value 10000.

3. Boost is calculated without proper scaling to user's token holdings

  • Results in disproportionate boost allocation

POC: Create a test file and add this poc and run it

// SPDX-License-Identifier: MIT
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 {
// Deploy contracts
raacToken = new RAACToken(owner, 100, 50);
veToken = new veRAACToken(address(raacToken));
boostController = new BoostController(address(veToken));
// Setup permissions
boostController.modifySupportedPool(pool, true);
raacToken.setMinter(owner);
// Mint RAAC tokens to users
raacToken.mint(attacker, 100e18); // Attacker gets 100 tokens
raacToken.mint(legitUser, 10000e18); // Legitimate user gets 10000 tokens
// Whitelist addresses
raacToken.manageWhitelist(attacker, true);
raacToken.manageWhitelist(legitUser, true);
raacToken.manageWhitelist(address(veToken), true);
}
function testHardcodedBaseVulnerability() public {
// Users lock tokens
vm.startPrank(legitUser);
raacToken.approve(address(veToken), type(uint256).max);
veToken.lock(10000e18, 365 days); // Lock 10000 tokens
vm.stopPrank();
vm.startPrank(attacker);
raacToken.approve(address(veToken), type(uint256).max);
veToken.lock(100e18, 365 days); // Lock 100 tokens
vm.stopPrank();
// Update boosts
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);
// Calculate boost per token
uint256 attackerBoostPerToken = (attackerBoost * 1e18) / veToken.balanceOf(attacker);
uint256 legitUserBoostPerToken = (legitUserBoost * 1e18) / veToken.balanceOf(legitUser);
// Verify attacker gets more boost per token
assertGt(
attackerBoostPerToken,
legitUserBoostPerToken,
"Attacker gets more boost per token"
);
// Verify boost ratio vs token ratio
uint256 boostRatio = (attackerBoost * 100) / legitUserBoost; // ~40%
uint256 tokenRatio = (veToken.balanceOf(attacker) * 100) / veToken.balanceOf(legitUser); // ~1%
assertGt(
boostRatio,
tokenRatio * 20,
"Attacker gets disproportionate boost relative to holdings"
);
// Calculate rewards
uint256 dailyRewards = 1000e18;
uint256 totalBoost = attackerBoost + legitUserBoost;
uint256 attackerRewards = (attackerBoost * dailyRewards) / totalBoost;
uint256 legitUserRewards = (legitUserBoost * dailyRewards) / totalBoost;
// Calculate reward efficiency (rewards per token held)
uint256 attackerRewardEfficiency = (attackerRewards * 1e18) / veToken.balanceOf(attacker);
uint256 legitUserRewardEfficiency = (legitUserRewards * 1e18) / veToken.balanceOf(legitUser);
assertGt(
attackerRewardEfficiency,
legitUserRewardEfficiency,
"Attacker gets more rewards per token held"
);
// Verify the core issue - small holder gets close to base amount boost
assertGt(
attackerBoost,
10000,
"Even small holder gets more than base amount"
);
}
}

Impact

Economic Impact:

  • Small holders receive disproportionately high boosts

  • Large holders get diminished returns on their holdings

  • Reward distribution becomes unfair

Attacker (100 tokens):
- Gets ~40% of boost power
- Receives ~40% of rewards
Legitimate User (10000 tokens):
- Has 100x more tokens
- But only gets ~2.5x more boost power
  • Undermines the incentive to hold large amounts of tokens

  • Creates unfair advantage for small holders

  • Could lead to gaming of the boost system

// Easy to exploit:
1. Get minimum tokens (100)
2. Call updateUserBoost()
3. Get disproportionate boost due to hardcoded 10000
4. Receive ~40% rewards while only holding 1% tokens

Tools Used

Recommendations

Use the userbalance instead of using an hardcoded amount .

Updates

Lead Judging Commences

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

BoostController::updateUserBoost uses hardcoded 10000 base amount, storing basis points instead of actual boosted amount

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

BoostController::updateUserBoost uses hardcoded 10000 base amount, storing basis points instead of actual boosted amount

Support

FAQs

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

Give us feedback!