Description
In FeeCollector.sol, the _calculateDistribution function implements an inefficient loop pattern that processes all 8 fee types regardless of their activity status, leading to unnecessary gas consumption.
Technical Details
function _calculateDistribution(uint256 totalFees) internal view returns (uint256[4] memory shares) {
uint256 totalCollected;
for (uint8 i = 0; i < 8; i++) {
uint256 feeAmount = _getFeeAmountByType(i);
if (feeAmount == 0) continue;
FeeType memory feeType = feeTypes[i];
totalCollected += feeAmount;
uint256 weight = (feeAmount * BASIS_POINTS) / totalFees;
shares[0] += (weight * feeType.veRAACShare) / BASIS_POINTS;
shares[1] += (weight * feeType.burnShare) / BASIS_POINTS;
shares[2] += (weight * feeType.repairShare) / BASIS_POINTS;
shares[3] += (weight * feeType.treasuryShare) / BASIS_POINTS;
}
}
Gas Analysis
-
Per Iteration Costs:
SLOAD (cold): 2100 gas
SLOAD (warm): 100 gas
Arithmetic ops: ~200 gas
Memory operations: ~100 gas
Total per iteration: ~2,500 gas
-
Worst Case Scenario:
feeTypes[0-6] = 0;
feeTypes[7] > 0;
Total waste = 7 * 2,500 = 17,500 gas
-
Real Usage Pattern:
feeTypes[0] > 0;
feeTypes[1] > 0;
feeTypes[2-7] = 0;
Wasted gas = 6 * 2,500 = 15,000 gas
Proof of Concept
contract GasTest is Test {
FeeCollector collector;
function testGasWaste() public {
collector.collectFee(1000e18, 0);
uint256 gasBefore = gasleft();
collector.distributeCollectedFees();
uint256 gasUsed = gasBefore - gasleft();
assertGt(gasUsed, expectedGasWithoutWaste + 15000);
}
}
Recommended Mitigation
contract FeeCollector {
uint8[] private activeFeeTypes;
mapping(uint8 => bool) private isActiveFeeType;
function _updateCollectedFees(uint256 amount, uint8 feeType) internal {
if (!isActiveFeeType[feeType]) {
activeFeeTypes.push(feeType);
isActiveFeeType[feeType] = true;
}
}
function _calculateDistribution(uint256 totalFees) internal view returns (uint256[4] memory shares) {
uint256 totalCollected;
for (uint256 i = 0; i < activeFeeTypes.length; i++) {
uint8 feeType = activeFeeTypes[i];
uint256 feeAmount = _getFeeAmountByType(feeType);
if (feeAmount == 0) continue;
}
for (uint256 i = activeFeeTypes.length; i > 0; i--) {
if (_getFeeAmountByType(activeFeeTypes[i-1]) == 0) {
isActiveFeeType[activeFeeTypes[i-1]] = false;
activeFeeTypes.pop();
}
}
}
}
Gas Savings
Only processes fee types with actual balances
Eliminates wasted iterations on empty fee types
Estimated savings: 12,000-15,000 gas per distribution
Scales with protocol activity
Additional Recommendations
Add fee type activity monitoring events
Implement fee type batch processing
Consider lazy fee type cleanup
Add gas consumption tests