The BaseGauge
contract implements a staking mechanism with boosted rewards based on veToken (voting escrow token) balances. Users can stake tokens and earn rewards, with their reward rate being boosted based on their veToken holdings.
In BaseGauge#earned
, rewards are calculated using getUserWeight
which depends on the user's veToken balance rather than their staked token balance. The boost multiplier in _applyBoost
is correctly calculated, but it's being applied to a base weight that doesn't consider the user's actual stake.
The _getBaseWeight
function returns the gauge's global weight from the controller instead of the user's staked balance. This means rewards are proportional to veToken holdings regardless of how many tokens are actually staked in the gauge.
High. Users can exploit this to earn disproportionate rewards by:
Staking minimal amounts in the gauge
Holding large amounts of veToken
This leads to unfair reward distribution and potential drain of reward tokens.
High. The economic incentive is significant, and the exploitation requires no special conditions or complex setup - just holding veToken and making a minimal stake.
Alice stakes 1 token in the gauge
Bob stakes 1000 tokens in the gauge
Alice holds 1000 veTokens
Bob holds 1 veToken
Despite Bob having 1000x more staked tokens, Alice earns more rewards due to her higher veToken balance
The key issue can be seen in the reward calculation flow:
earned
calls getUserWeight
getUserWeight
gets base weight from _getBaseWeight
which returns global gauge weight
_applyBoost
multiplies this by veToken-based boost
User's actual stake (_balances[account]
) is never considered
Modify _getBaseWeight
to return the user's staked balance:
This ensures rewards are primarily based on staked amounts, with veToken balance only affecting the boost multiplier as intended.
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.