A vulnerability in GaugeController.sol an attacker can skew gauge reward distribution by voting with a maximum weight (100%) just before a period ends and retracting the vote after the new period begins. This causes distributeRewards to use of a temporarily inflated g.weight instead of a time-weighted average (TimeWeightedAverage.calculateAverage), resulting in a single gauge receiving a disproportionately large share of rewards (e.g., 500,000 ether from a 1M ether emission for RWA gauges) compared to other gauges (e.g., 89,275 ether from a 250K ether emission for RAAC gauges). This flaw leads to unfair reward allocation, reducing rewards for legitimate users in the RAAC ecosystem.
The vulnerability arises from a lack of synchronization between the voting mechanism and reward distribution:
Vote function (https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/gauges/GaugeController.sol#L190): Instantly updates g.weight based on votingPower from veRAACToken:
distributeRewards function (https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/gauges/GaugeController.sol#L323): Calculates rewards using the current g.weight without enforcing a time-weighted average:
_calculateReward function (https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/gauges/GaugeController.sol#L360)
Period Management: updatePeriod is not required before distributeRewards, allowing attackers to exploit the window between periods with an inflated g.weight.
Exploit Conditions:
The attacker needs a modest amount of veRAACToken (e.g., 100 ether).
Precise timing to call vote just before the period ends (e.g., 1 hour prior), achievable with a bot.
Calling distributeRewards immediately after the new period starts to leverage the inflated g.weight.
Observed Exploit:
Attacker votes 100% weight (10000 basis points) for rwaGauge with 100 ether of veRAACToken, skewing totalWeight to 1M ether.
distributeRewards yields ~500,000 ether for rwaGauge (50% of 1M ether RWA emission).
A legitimate user voting 50% weight (5000 basis points) with 500 ether for raacGauge later receives only ~89,275 ether from a 250K ether RAAC emission due to diluted influence.
Proof of Code:
Updated Test File (test/unit/core/governance/gauges/GaugeController.test.js):
GaugeController
Integration Tests
Reward for rwaGauge (attacker): 500000.0
Reward for raacGauge (user2): 89275.0
Unfair Reward Distribution: The attacker's gauge (rwaGauge) receives an outsized share (~500,000 ether), while legitimate gauges (raacGauge) receive significantly less (~89,275 ether), disrupting equitable allocation.
Financial Loss: Legitimate users staking veRAACToken lose substantial RWA and RAAC rewards (e.g., reduced from a potential 125K ether to 89K ether for RAAC gauges), impacting their profits.
Loss of Trust: Unreliable reward distribution undermines confidence in the system, reducing staking incentives.
Repeatability: The exploit can be repeated each period with low cost (minimal veRAACToken and gas), causing ongoing damage.
Manual Review.
Hardhat.
To mitigate this vulnerability, synchronize reward distribution with time-weighted averages and restrict voting near period boundaries:
Synchronize with TimeWeightedAverage:
Require updatePeriod Before distributeRewards:
Restrict Voting Near Period End
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.