Core Contracts

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

The workingSupply of a pool is incorrectly overwritten (instead of accumulated) when users update their boosts.

Author Revealed upon completion

Summary:

In the updateUserBoost function, workingSupply should represent the sum of all user boosts in the pool. Instead, it’s set to the latest user’s boost amount, discarding previous contributions.

// Overwrites workingSupply with latest user boost
poolBoost.workingSupply = newBoost;

Vulnerability Details:

The userBoosts mapping stores each user's boost individually, per pool. However, the poolBoost.workingSupply is incorrectly being overwritten with the latest user's boost, disregarding all prior contributions.

POC

I created a Test using below scenario's to back my claim:

  1. Add a supported pool.

  2. User1 updates their boost to 100.

  3. User2 updates their boost to 200.

  4. Verify the pool’s workingSupply is 300 (100 + 200).

// Test Setup
address admin = address(0x1);
BoostController boostController = new BoostController(veTokenAddress);
address pool = address(0x2);
address user1 = address(0x3);
address user2 = address(0x4);

// Grant roles and add pool
vm.prank(admin);
boostController.grantRole(MANAGER_ROLE, admin);

vm.prank(admin);
boostController.modifySupportedPool(pool, true); // Add pool

// Simulate User1 updating boost to 100
vm.prank(user1);
boostController.updateUserBoost(user1, pool);

// Check workingSupply after User1’s update
(uint256 totalBoost, uint256 workingSupply, , ) = boostController.getPoolBoost(pool);
assertEq(workingSupply, 100);

// Simulate User2 updating boost to 200
vm.prank(user2);
boostController.updateUserBoost(user2, pool);

// Check workingSupply after User2’s update
(, workingSupply, , ) = boostController.getPoolBoost(pool);
assertEq(workingSupply, 300); /

Test Result

[Test Result]
Test Steps │ Actual workingSupply │ Expected │ Pass/Fail
────────────────────────┼───────────────────────┼──────────┼───────────
After User1’s update │ 100100 │ ✅
After User2’s update │ 200300 │ ❌

The workingSupply is overwritten to 200 (User2’s boost) instead of accumulating to 300.

Impact:

If the workingSupply is incorrect, reward distributions will be miscalculated. Users might get more or less than they deserve, depending on the order of updates. This undermines the trust in the protocol's fairness.

Using the POC above, If the pool has 300 total boosted tokens but workingSupply  shows 200, rewards will be distributed as if there are only 200 tokens.

Tools Used: Foundry 

Recommendations:

Update workingSupply to accumulate boosts instead of overwriting:

// In updateUserBoost():
if (newBoost >= oldBoost) {
poolBoost.workingSupply += (newBoost - oldBoost);
} else {
poolBoost.workingSupply -= (oldBoost - newBoost);
}
//Remove: poolBoost.workingSupply = newBoost;

Support

FAQs

Can’t find an answer? Join our Discord or follow us on Twitter.

Cyfrin
Updraft
CodeHawks
Solodit
Resources