Summary
GaugeController has a distributeRevenue function which is supposed to tranfer the performance fee to FeeCollector. However the fee is never sent and not stored in state variable created for this purpose.
Vulnerability Details
The distributeRevenue splits the revenue between veRAACToken holders and performance fee that is meant to be sent to FeeCollector. We can see that revenueShares is increased but performanceFees state variable is not.
function distributeRevenue(
GaugeType gaugeType,
uint256 amount
) external onlyRole(EMERGENCY_ADMIN) whenNotPaused {
if (amount == 0) revert InvalidAmount();
uint256 veRAACShare = amount * 80 / 100;
uint256 performanceShare = amount * 20 / 100;
revenueShares[gaugeType] += veRAACShare;
_distributeToGauges(gaugeType, veRAACShare);
emit RevenueDistributed(gaugeType, amount, veRAACShare, performanceShare);
}
Calculated performanceFee is also not sent in _distributeToGauges function which is called by distributeRevenue.
function _distributeToGauges(
GaugeType gaugeType,
uint256 amount
) internal {
uint256 totalTypeWeight = 0;
uint256[] memory gaugeWeights = new uint256[](_gaugeList.length);
uint256 activeGaugeCount = 0;
for (uint256 i = 0; i < _gaugeList.length; i++) {
address gauge = _gaugeList[i];
if (gauges[gauge].isActive && gauges[gauge].gaugeType == gaugeType) {
gaugeWeights[i] = gauges[gauge].weight;
totalTypeWeight += gaugeWeights[i];
activeGaugeCount++;
}
}
if (totalTypeWeight == 0 || activeGaugeCount == 0) return;
for (uint256 i = 0; i < _gaugeList.length; i++) {
address gauge = _gaugeList[i];
if (gauges[gauge].isActive && gauges[gauge].gaugeType == gaugeType) {
uint256 gaugeShare = (amount * gaugeWeights[i]) / totalTypeWeight;
if (gaugeShare > 0) {
IGauge(gauge).notifyRewardAmount(gaugeShare);
}
}
}
}
Impact
performanceFees is not increased and fee is not sent. The fees are lost in the GaugeController due to missing implementation.
Tools Used
Manual Review, Hardhat
Recommendations
Send performanceFee to FeeCollector.