Core Contracts

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

Pool boost working supply is incorrectly updated

Summary

Using assignment instead of adding value when updating pool boost working supply in the function BoostController::updateUserBoost() causes pool boost working supply to be incorrectly updated.

Vulnerability Details

According to natspec, PoolBoost.workingSupply is The total working supply including boosts. However, in the function BoostController::updateUserBoost() the value poolBoost.workingSupply is set directly to the value newBoost, which is the boost amount of the user account, not the new boost amount of the pool boost. This causes the poolBoost.workingSupply to be updated incorrectly.

* @notice Struct to track pool-wide boost metrics
* @param totalBoost The total boost amount for the pool
* @param workingSupply The total working supply including boosts
* @param baseSupply The base supply without boosts
* @param lastUpdateTime The last time pool boosts were updated
*/
struct PoolBoost {
uint256 totalBoost;
uint256 workingSupply;
uint256 baseSupply;
uint256 lastUpdateTime;
}
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;
// Calculate new boost based on current veToken balance
uint256 newBoost = _calculateBoost(user, pool, 10000); // Base amount
userBoost.amount = newBoost;
userBoost.lastUpdateTime = block.timestamp;
// Update pool totals safely
if (newBoost >= oldBoost) {
poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
} else {
poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
}
@> poolBoost.workingSupply = newBoost; // Set working supply directly to new boost
poolBoost.lastUpdateTime = block.timestamp;
emit BoostUpdated(user, pool, newBoost);
emit PoolBoostUpdated(pool, poolBoost.totalBoost, poolBoost.workingSupply);
}

PoC

Add the test to test/unit/core/governance/boost/BoostController.test.js

describe("Boost Calculations", () => {
// ...
it.only("pool boost working supply incorrectly updated", async () => {
// @audit PoC pool boost working supply incorrectly updated
// Initial boost setup
const poolAddr = mockPool.target
// user2 updates user boost
await boostController.connect(user2).updateUserBoost(user1.address, poolAddr);
// record initial working supply
const initialWorkingSupply = (await boostController.getPoolBoost(poolAddr))[1];
// user1 has 50 veToken balance
await veToken.setBalance(user1.address, ethers.parseEther("50"))
// user1 updates user boost
await boostController.connect(user1).updateUserBoost(user1.address, poolAddr);
const finalWorkingSupply = (await boostController.getPoolBoost(poolAddr))[1];
expect(finalWorkingSupply).to.gt(initialWorkingSupply) // working supply should increase
});

Run the test and console shows:

BoostController
Boost Calculations
1) pool boost working supply incorrectly updated
0 passing (2s)
1 failing
1) BoostController
Boost Calculations
pool boost working supply incorrectly updated:
AssertionError: expected 10365 to be above 14999.
+ expected - actual
-10365
+14999

Impact

  • Pool boost working supply is incorrectly updated

Tools Used

Manual

Recommendations

N/A

Updates

Lead Judging Commences

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

BoostController::updateUserBoost overwrites workingSupply with single user's boost value instead of accumulating, breaking reward multipliers and allowing last updater to capture all benefits

Support

FAQs

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