Core Contracts

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

No Revert for New Users in updateUserBoost May Allow Unintended First-Time Updates

Summary

The updateUserBoost function in the BoostController contract doesn’t stop (or "revert") when a new user calls it for the first time. It lets them update their boost even if they’ve never used it before for a pool, which might be okay but could also mean the system isn’t checking new users as strictly as it should.

Vulnerability Details

The function is open for everyone, including new users.

function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
if (paused()) revert EmergencyPaused();
if (user == address(0)) revert InvalidPool();
if (!supportedPools[pool]) revert PoolNotSupported();
UserBoost storage userBoost = userBoosts[user][pool];
PoolBoost storage poolBoost = poolBoosts[pool];
uint256 oldBoost = userBoost.amount;
uint256 newBoost = _calculateBoost(user, pool, 10000); // Base amount
userBoost.amount = newBoost;
userBoost.lastUpdateTime = block.timestamp;
if (newBoost >= oldBoost) {
poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
} else {
poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
}
poolBoost.workingSupply = newBoost;
poolBoost.lastUpdateTime = block.timestamp;
emit BoostUpdated(user, pool, newBoost);
emit PoolBoostUpdated(pool, poolBoost.totalBoost, poolBoost.workingSupply);
}

When a new user calls this, userBoosts[user][pool] is empty.

  • If the contract is paused (if (paused())).

  • If the user’s address is 0 (if (user == address(0))), which doesn’t happen for a real person calling it.

  • If the pool isn’t supported (if (!supportedPools[pool])), which isn’t about being a new user—it’s about the pool.

  • Boost Calculation: It calls _calculateBoost:

    function _calculateBoost(address user, address pool, uint256 amount) internal view returns (uint256) {
    if (amount == 0) revert InvalidBoostAmount();
    if (!supportedPools[pool]) revert PoolNotSupported();
    uint256 userBalance = IERC20(address(veToken)).balanceOf(user);
    uint256 totalSupply = IERC20(address(veToken)).totalSupply();
    if (userBalance == 0 || totalSupply == 0) {
    return amount;
    }
    // ... (boost calculation logic)
    }
    • Here, amount is 10,000, so it doesn’t stop.

    • If the new user has no veTokens (userBalance = 0), it still works—it just returns 10,000. No stopping.

    • If they have veTokens, it calculates a higher boost (like 15,000). Still no stopping.

    • oldBoost is 0 for a new user.

    • newBoost is at least 10,000 (or more with veTokens).

    • The function updates userBoost.amount and pool totals without any trouble.

Impact

  1. New users can jump in and add their boost to a pool right away, even if they haven’t done anything else in the system. If the protocol wanted them to do something first (like stake tokens or delegate), this skips that step.

  2. Their boost gets added to totalBoost and sets workingSupply, which could surprise other users or systems expecting only experienced users to affect the pool.

Tools Used

Recommendations

Make sure users have veTokens first. Add this in updateUserBoost

if (IERC20(address(veToken)).balanceOf(user) == 0) revert NoVeTokens();

This stops users with no veTokens from updating, so only active users can join in.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!