The vulnerability arises from an incorrect calculation of the boost multiplier—resulting in rewards that do not fairly reflect the user's intended boost.
Below is an example that walks through the calculation performed by the getBoostMultiplier function
MIN_BOOST: 10,000 (representing a 1× multiplier)
MAX_BOOST: 25,000 (representing a 2.5× multiplier)
supportedPools[pool]: true (the pool is supported)
Suppose the user’s boost record for this pool is stored in the UserBoost struct and:
userBoost.amount: 15,000
Calculating the Baseline (baseAmount):
The code then calculates a “baseAmount”:
Substituting the values:
baseAmount = (15,000×10,000) / 25,000
baseAmount=6,000
Calculating the Boost Multiplier:
Finally, the boost multiplier is calculated as:
Substituting our numbers:
return value = (15,000×10,000) / 6,000
return value=25,000
This result means the boost multiplier is 25,000 basis points (or 2.5×)
In this calculation, any nonzero userBoost.amountwill always cancel out when applying the formula.
Therefore, as long as userBoost.amountis nonzero, the calculation always returns MAX_BOOST.
The formula, as written, effectively returns the maximum boost multiplier for any nonzero userBoost.amount.
Manual Review
We introduce a normalization constant (here called MAX_USER_BOOST) that represents the maximum boost amount a user can have. The new formula is:
boostMultiplier= MIN_BOOST+(((MAX_BOOST−MIN_BOOST)×userBoost.amount)) / MAX_USER_BOOST)
This means that when a user’s recorded boost amount is 0, the multiplier is exactly MIN_BOOST (i.e. 1x), and when the user’s boost amount reaches MAX_USER_BOOST, the multiplier becomes MAX_BOOST (e.g. 2.5x). Values in between are linearly interpolated.
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.