Core Contracts

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

Missing Cross‑Module Call Flow for “Revenue Distribution” in FeeCollector ↔ GaugeController

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/gauges/GaugeController.sol#L511

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/collectors/FeeCollector.sol#L180

The system’s doc claims partial or automatic revenue distribution to gauge stakers, but no code path calls the GaugeController’s distributeRevenue(...) from the FeeCollector (or anywhere else). Hence, the synergy for fee distribution across the entire protocol is incomplete or missing, creating a dead portion of the code. Implementing a bridging function or clarifying a manual admin approach ensures the real “80%/20%” revenue share design is actually enforced.

Overview

  1. Fee Collector & Revenue

  2. GaugeController’s distributeRevenue(...)

  3. No Bridge from Fee Collector to Gauges

    • Nowhere in the code is there a cross‑call from the FeeCollector to the GaugeController’s distributeRevenue(...).

    • The FeeCollector never references the gauge or gauge controller.

    • The GaugeController’s distributeRevenue(...) is never invoked by the FeeCollector.

    • So the “80% to veRAAC, 20% yield/performance” logic the doc references in multiple places is effectively never triggered or only triggers if some admin manually calls distributeRevenue(...).

Impact

  1. Dead “Revenue Distribution” Feature
    Even though the code is built to handle “revenue distribution” to gauges or veRAAC holders, there is no actual code path or function that flows from the FeeCollector (where fees accumulate) over to the GaugeController. That means no automatic or triggered revenue sharing per the design.

  2. Contradiction of Documentation
    The official doc or code comments say “the protocol revenue is 80% to veRAAC holders, 20% to performance fees (or yield).” In reality, that synergy is never realized—no system component calls the gauge’s distribution function, so the fees remain in the FeeCollector or get stuck in the system unless manually moved by an admin.

  3. Admin-Only Workaround
    If the design requires an admin or “EMERGENCY_ADMIN” to call distributeRevenue(...) with some arbitrary amount, that must be documented and consistently used. As is, the code does not show any link from fee accumulation to gauge distribution. This can lead to indefinite hoarding of fees or inconsistent distribution that depends on manual steps.

  4. Protocol Confusion
    Users reading the doc about “80% protocol fees allocated to veRAAC holders through gauge distribution” expect an automatic or at least a clear flow. The code includes no bridging call, causing user confusion or an incomplete reward mechanism.

Concrete Example / Attack Path

While not a direct exploit that grants an attacker funds, the result is that:

  1. Fees Accrue in the FeeCollector.

  2. The system claims some portion is “destined for gauge rewards or veRAAC holders.”

  3. Since the code never transfers from FeeCollector to the gauge, no user or gauge sees those fees.

  4. Possibly an unscrupulous admin can keep these fees unclaimed or funnel them elsewhere, contradicting the design that automatically or regularly shares them with participants.

Recommendation

  1. Implement Automatic Flow

    • In the FeeCollector’s distributeCollectedFees() or a new function, call something like:

      gaugeController.distributeRevenue(GaugeType.RAAC, raacShare);
      gaugeController.distributeRevenue(GaugeType.RWA, rwaShare);
    • This ensures the code actually moves the fees from FeeCollector to the gauge system as documented.

  2. Manual Flow

    • If the design wants a manual approach, clarify in doc that “an admin must pull fees from FeeCollector and call gaugeController.distributeRevenue(...),” ensuring a publicly known procedure.

    • Possibly define an AccessControl role that calls FeeCollector.transfer(...) to the gauge if that is the intended design.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

GaugeController.distributeRevenue calculates 20% performance fee but never transfers or allocates it to any recipient, causing loss of funds

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

GaugeController.distributeRevenue calculates 20% performance fee but never transfers or allocates it to any recipient, causing loss of funds

Support

FAQs

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

Give us feedback!