The distributeRewards
function is designed to calculate and transfer rewards to a gauge based on its weight. However, while the function correctly computes the reward amount and calls the gauge’s notifyRewardAmount
method, it does not perform any token transfer. As a result, gauges receive state updates that imply rewards have been distributed, but no actual reward tokens are transferred from the BaseGauge contract, leading to a discrepancy between documented behavior and on-chain state.
Lack of Actual Token Transfer:
The function calculates the reward:
and then calls:
However, no transfer of reward tokens (using, for example, rewardToken.safeTransfer(...)
) occurs. This means that while the gauge’s internal state is updated to reflect a new reward amount, the reward tokens remain in the BaseGauge contract.
Misleading Documentation:
The function’s comments state that it "Calculates and transfers rewards based on gauge weight," yet the implementation only updates the gauge's state via notifyRewardAmount
.
False Reward Allocation:
Gauges may display an updated reward rate internally, suggesting that rewards have been allocated, while the reward tokens remain in the BaseGauge contract. This misalignment can result in a situation where users expect rewards that have not been made available.
Scenario Setup:
Deploy the BaseGauge contract along with its dependencies, including the reward token and a gauge contract implementing the IGauge
interface.
Ensure the gauge is registered (i.e., isGauge(gauge)
returns true) and marked as active.
Exploit Steps:
Step 1: Call the distributeRewards
function with the gauge’s address.
Step 2: The function computes a non-zero reward value using _calculateReward(gauge)
.
Step 3: It then calls IGauge(gauge).notifyRewardAmount(reward)
which updates the gauge's reward state.
Step 4: Verify that no reward tokens are transferred from the BaseGauge contract to the gauge.
Observation:
The gauge’s internal state indicates that it has been notified of a reward amount.
However, a check of token balances confirms that the reward tokens remain in the BaseGauge contract, demonstrating that the actual token transfer did not occur.
Manual review
Implement Actual Token Transfer:
Update the distributeRewards
function to perform an explicit transfer of reward tokens to the gauge before calling the notification function:
This ensures that the gauge receives the actual tokens corresponding to the calculated reward.
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.