n the updateUserBoost
function, the pool’s working supply is updated by overwriting it with the boost value of the most recently updated user rather than accumulating (summing) the boosts of all users in the pool. This behavior is contrary to the intended Curve‑style boost mechanics described in the docs, where the working supply should reflect the aggregate effective boost of all users.
In the BaseGauge contract, rewards are distributed proportionally based on each user’s effective “weight.” That effective weight is computed as follows:
The BaseGauge retrieves a user’s base weight using _getBaseWeight(account)
(which typically reflects the staked amount or gauge weight).
It then applies a boost multiplier via _applyBoost(account, baseWeight)
.
In _applyBoost
, boost parameters (like maxBoost
, minBoost
, and boostWindow
) are obtained from the contract’s internal state. In particular, boost-related data such as the total weight, total voting power, and the user’s own voting power play a role in calculating the multiplier through a call to the BoostCalculator library.
The boost multiplier used in this calculation is expected to be influenced by the aggregate boost contributions of all users in the pool (i.e. the pool’s “working supply”). This aggregate is supposed to come from data updated via BoostController’s updateUserBoost
function.
In the BoostController’s updateUserBoost
function, the pool’s boost totals are updated in two parts:
The totalBoost is updated cumulatively—adjusting the previous total by the difference between the new and old boost of a given user.
In contrast, workingSupply is set to the newBoost
value directly. This means that when a user (say, user2) updates their boost after another user (user1) has already updated theirs, the working supply becomes just user2’s boost.
The cumulative working supply (i.e. the sum of the boosted contributions of all users in the pool) is not maintained.
Underestimated Denominator:
If the pool’s working supply is merely the last updated user’s boost, then the total working supply is underreported compared to the intended cumulative sum of all users’ boosts.
Disproportionate Rewards:
The last user who updates their boost (or a malicious actor who deliberately updates last) will see the working supply set equal to their own boost. As a result, when calculating reward shares, that user’s weight will be divided by a smaller denominator, causing them to receive a much larger proportion of the rewards than their actual stake justifies.
Economic Exploitation:
A malicious user could strategically wait until other users update their boosts, then call updateUserBoost
to overwrite the pool’s working supply with only their boost. This manipulation would distort the reward calculations, enabling the attacker to capture a disproportionate share of the rewards, undermining fairness and potentially causing economic losses for honest participants.
Manual Review, Hardhat.
Add the test to "test/unit/core/governance/boost/BoostController.test.js", on the describe block `Boost Calculations`.
Logs:
One possible mitigation would be changing updateUserBoost
function on BoostController
to:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.