Core Contracts

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

Missing gauge period reset in controller leads to reward distribution failure

Relevant Context

The GaugeController manages reward distribution to various gauges in the system. Each gauge has a period mechanism that tracks distributed rewards and enforces reward caps. The BaseGauge contract implements the core period and reward distribution logic.

Finding Description

The GaugeController#updatePeriod function fails to call IGauge(gauge).updatePeriod() when rolling over to a new period. This means the gauge's internal period state, specifically periodState.distributed, is never reset to 0.

Furthermore, the architecture provides no mechanism for the controller to trigger a gauge's period update - the updatePeriod function in GaugeController only updates its own internal period tracking, but has no way to trigger the corresponding update in the gauge contract.

In BaseGauge, the notifyRewardAmount function enforces that total distributed rewards cannot exceed the period emission cap through the check:

if (amount + state.distributed > state.emission) {
revert RewardCapExceeded();
}

Since distributed is never reset, it will eventually reach the emission cap, causing all subsequent reward distributions to revert.

Impact Explanation

High. Once a gauge's distributed amount reaches the emission cap, that gauge can no longer receive any rewards. This effectively breaks the core reward distribution mechanism for affected gauges. The issue is particularly severe because there is no architectural pathway to trigger a period reset from the controller.

Likelihood Explanation

High. This will affect every gauge once enough rewards have been distributed to reach their emission caps. The issue is deterministic and will occur in normal operation of the protocol.

Proof of Concept

  1. A gauge is added to the system with an emission cap of 1000 tokens per period

  2. Over several reward distributions, periodState.distributed accumulates to 900 tokens

  3. Controller attempts to distribute 200 more tokens via notifyRewardAmount

  4. Transaction reverts because 900 + 200 > 1000 (emission cap)

  5. All subsequent reward distributions to this gauge will fail

  6. Due to architectural limitations, there is no way for the controller to reset the gauge's period state

Recommendation

The system needs both an immediate fix and an architectural improvement:

  1. Add a method to trigger gauge period updates:

interface IGauge {
+ function updatePeriod() external;
// ... other functions
}
  1. Modify GaugeController#updatePeriod to use this interface:

function updatePeriod(address gauge) external override whenNotPaused {
Gauge storage g = gauges[gauge];
if (!g.isActive) revert GaugeNotActive();
+ // Update gauge's internal period first
+ IGauge(gauge).updatePeriod();
TimeWeightedAverage.Period storage period = gaugePeriods[gauge];
// Rest of the function...
}

This ensures proper synchronization between controller and gauge periods, allowing the reward distribution system to function as intended.

Updates

Lead Judging Commences

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

GaugeController::updatePeriod doesn't call the gauge's updatePeriod function, preventing periodState.distributed from resetting and eventually causing distributeRewards to permanently fail

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

GaugeController::updatePeriod doesn't call the gauge's updatePeriod function, preventing periodState.distributed from resetting and eventually causing distributeRewards to permanently fail

Support

FAQs

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

Give us feedback!