59931 sc insight useless check

Submitted on Nov 17th 2025 at 02:39:48 UTC by @Le_Rems for Audit Comp | Firelight

  • Report ID: #59931

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/firelight-protocol/firelight-core/blob/main/contracts/FirelightVault.sol

  • Impacts:

Description

Brief/Intro

The periodConfigurationAtNumber() function in FirelightVault contains a redundant validation check that will never execute. The function checks if periodConfiguration.epoch == 0 and reverts, but this condition is impossible to reach given the function's loop logic and how period configurations are initialized. While this redundant check doesn't cause functional issues or fund loss, it wastes gas on every call.

Vulnerability Details

The FirelightVault contract manages a dynamic array of PeriodConfiguration structures that define period boundaries over time. Each configuration contains an epoch , duration , and startingPeriod . These configurations are added during initialization and can be updated over time by users with the PERIOD_CONFIGURATION_UPDATE_ROLE.

The periodConfigurationAtNumber() function retrieves the appropriate period configuration for a given period number by iterating through the periodConfigurations array:

function periodConfigurationAtNumber(uint256 periodNumber) external view returns (PeriodConfiguration memory) {
    uint256 length = periodConfigurations.length;
    if (length == 0) revert InvalidPeriod();

    PeriodConfiguration memory periodConfiguration;
    for (uint256 i = 0; i < length; i++) {
        if (periodNumber < periodConfigurations[i].startingPeriod)
            break;
        periodConfiguration = periodConfigurations[i];
    }
@>  if (periodConfiguration.epoch == 0) revert InvalidPeriod();
    return periodConfiguration;
}

The issue lies in the validation check. To understand why this check is unreachable, we must trace through the function's execution:

  1. Initial state: periodConfiguration is declared as a memory struct. In Solidity, uninitialized structs have all fields set to zero values, so initially periodConfiguration.epoch = 0.

  2. First configuration guarantee: The contract ensures periodConfigurations.length >= 1 after initialization. During initialize(), the contract always calls _addPeriodConfiguration(Time.timestamp(), initParams.periodConfigurationDuration), which adds the first configuration. This first configuration always has startingPeriod = 0 as set in _addPeriodConfiguration().

  3. Loop execution: The loop starts at i = 0. For the first iteration:

    • The condition checks: periodNumber < periodConfigurations[0].startingPeriod

    • Since periodConfigurations[0].startingPeriod = 0 and periodNumber is a uint256 (always >= 0), the condition periodNumber < 0 is always false

    • Therefore, the loop body executes: periodConfiguration = periodConfigurations[0]

    • This assignment sets periodConfiguration.epoch to periodConfigurations[0].epoch, which is Time.timestamp() from initialization (guaranteed to be non-zero as it's a real block timestamp)

  4. Epoch validation: The epoch values are further protected by validation in _addPeriodConfiguration():

    • For the first configuration: if (newEpoch < Time.timestamp()) revert InvalidPeriodConfigurationEpoch(); ensures the epoch is at least the current timestamp (non-zero)

    • For subsequent configurations: if (newEpoch < nextPeriodEnd() || ...) ensures the epoch is always a valid future timestamp

  5. Unreachable check: Since the loop is guaranteed to assign at least periodConfigurations[0] (which has a non-zero epoch), periodConfiguration.epoch can never be 0.

Impact Details

The impact of this finding is primarily informational, affecting code quality rather than functionality:

1. Gas Waste (Low Impact - Operational):

  • Every call to periodConfigurationAtNumber() performs an unnecessary sload operation to read periodConfiguration.epoch and a comparison check

  • While minimal per call (~100-200 gas), this accumulates over time and is wasteful

References

  • periodConfigurationAtNumber() implementation

  • _addPeriodConfiguration() function showing epoch validation

  • initialize() function showing first configuration is always added

  • periodConfigurationAtTimestamp() showing identical redundant check pattern

Proof of Concept

Proof of Concept

Was this helpful?