Core Contracts

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

period misalignment leads to incorrect weight calculations

Summary

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

period misalignment leads to incorrect weight calculations

Vulnerability Details

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

/**
* @notice Calculates reward amount for a gauge
* @dev Uses gauge weight and type weight to determine share
* @param gauge Address of gauge to calculate reward for
* @return Calculated reward amount
*/
function _calculateReward(address gauge) internal view returns (uint256) {
Gauge storage g = gauges[gauge];
uint256 totalWeight = getTotalWeight();
if (totalWeight == 0) return 0;
uint256 gaugeShare = (g.weight * WEIGHT_PRECISION) / totalWeight;
uint256 typeShare = (typeWeights[g.gaugeType] * WEIGHT_PRECISION) / MAX_TYPE_WEIGHT;
// Calculate period emissions based on gauge type
uint256 periodEmission = g.gaugeType == GaugeType.RWA ? _calculateRWAEmission() : _calculateRAACEmission();
return (periodEmission * gaugeShare * typeShare) / (WEIGHT_PRECISION * WEIGHT_PRECISION);
}
// In the contract, periods are set differently for RWA and RAAC:
uint256 duration = g.gaugeType == GaugeType.RWA ? 30 days : 7 days;
// RWA calculations happen every 30 days
if (g.gaugeType == GaugeType.RWA) {
// Weight updates every 30 days
updatePeriod(gauge); // at t = 30 days
}
// RAAC calculations happen every 7 days
if (g.gaugeType == GaugeType.RAAC) {
// Weight updates every 7 days
updatePeriod(gauge); // at t = 7, 14, 21, 28 days
}
function _calculateReward(address gauge) internal view returns (uint256) {
// RWA emissions are monthly (30 days)
uint256 periodEmission = g.gaugeType == GaugeType.RWA ?
1000000 * 10**18 : // 30 days
250000 * 10**18; // 7 days
// This creates uneven distribution because:
// RWA: 1,000,000 tokens per 30 days
// RAAC: 250,000 * 4 = 1,000,000 tokens per 28 days
// 2-day gap in alignment
}
// In updatePeriod:
TimeWeightedAverage.Period storage period = gaugePeriods[gauge];
// For RWA at day 30:
if (g.gaugeType == GaugeType.RWA) {
// Gets full 30-day period weight
uint256 average = TimeWeightedAverage.calculateAverage(period, block.timestamp);
}
// For RAAC at day 28:
if (g.gaugeType == GaugeType.RAAC) {
// Missing 2 days of weight data compared to RWA
uint256 average = TimeWeightedAverage.calculateAverage(period, block.timestamp);
}
// After 90 days:
RWA Periods: 0 -> 30 -> 60 -> 90
RAAC Periods: 0 -> 7 -> 14 -> 21 -> 28 -> 35 -> 42 -> 49 -> 56 -> 63 -> 70 -> 77 -> 84
// RWA has 3 periods
// RAAC has 12 complete periods + 6 days
// Misalignment grows over time

Impact

period misalignment leads to incorrect weight calculations

Tools Used

Foundry

Recommendations

Copy// 1. Standardize on common base period
uint256 constant BASE_PERIOD = 7 days;
uint256 constant RWA_MULTIPLIER = 4; // 28 days
function updatePeriod(address gauge) external {
Gauge storage g = gauges[gauge];
uint256 periods = g.gaugeType == GaugeType.RWA ? RWA_MULTIPLIER : 1;
uint256 duration = BASE_PERIOD * periods;
// Now RWA aligns with RAAC every 28 days
TimeWeightedAverage.createPeriod(
period,
block.timestamp,
duration,
startWeight,
endWeight
);
}
// 2. Adjust emissions to match periods
function _calculateEmission(GaugeType gaugeType) internal pure returns (uint256) {
// Base emission rate per 7 days
uint256 baseEmission = 250000 * 10**18;
return gaugeType == GaugeType.RWA ?
baseEmission * RWA_MULTIPLIER : // 28 days worth
baseEmission; // 7 days worth
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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

Give us feedback!