Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Invalid

Gas Optimization Analysis: Fee Distribution Loop

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;
// Inefficient: Fixed 8 iterations regardless of active fees
for (uint8 i = 0; i < 8; i++) {
uint256 feeAmount = _getFeeAmountByType(i);
if (feeAmount == 0) continue; // Still costs gas for SLOAD
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

  1. 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
  2. Worst Case Scenario:

    // All fee types empty except last one
    feeTypes[0-6] = 0; // 7 wasted iterations
    feeTypes[7] > 0; // Only useful iteration
    Total waste = 7 * 2,500 = 17,500 gas
  3. Real Usage Pattern:

    // Typical active fee types
    feeTypes[0] > 0; // Protocol fees
    feeTypes[1] > 0; // Lending fees
    feeTypes[2-7] = 0; // 6 empty iterations
    Wasted gas = 6 * 2,500 = 15,000 gas

Proof of Concept

contract GasTest is Test {
FeeCollector collector;
function testGasWaste() public {
// Setup with only protocol fees
collector.collectFee(1000e18, 0);
uint256 gasBefore = gasleft();
collector.distributeCollectedFees();
uint256 gasUsed = gasBefore - gasleft();
// Around 15,000 gas wasted on empty iterations
assertGt(gasUsed, expectedGasWithoutWaste + 15000);
}
}

Recommended Mitigation

contract FeeCollector {
// Add active fee type tracking
uint8[] private activeFeeTypes;
mapping(uint8 => bool) private isActiveFeeType;
function _updateCollectedFees(uint256 amount, uint8 feeType) internal {
if (!isActiveFeeType[feeType]) {
activeFeeTypes.push(feeType);
isActiveFeeType[feeType] = true;
}
// ... existing fee update code ...
}
function _calculateDistribution(uint256 totalFees) internal view returns (uint256[4] memory shares) {
uint256 totalCollected;
// Only iterate over active fee types
for (uint256 i = 0; i < activeFeeTypes.length; i++) {
uint8 feeType = activeFeeTypes[i];
uint256 feeAmount = _getFeeAmountByType(feeType);
if (feeAmount == 0) continue;
// ... existing distribution logic ...
}
// Clear inactive fee types
for (uint256 i = activeFeeTypes.length; i > 0; i--) {
if (_getFeeAmountByType(activeFeeTypes[i-1]) == 0) {
isActiveFeeType[activeFeeTypes[i-1]] = false;
activeFeeTypes.pop();
}
}
// ... rest of the function ...
}
}

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

  1. Add fee type activity monitoring events

  2. Implement fee type batch processing

  3. Consider lazy fee type cleanup

  4. Add gas consumption tests

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!