59296 sc low periodattimestamp uint48 timestamp ignores its parameter and always returns the current period

Submitted on Nov 10th 2025 at 20:19:08 UTC by @edantes for Audit Comp | Firelight

  • Report ID: #59296

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts:

    • Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)

    • Theft of gas

Description

Brief/Intro

The read-only helper periodAtTimestamp(uint48 timestamp) is supposed to return the period number for an arbitrary timestamp. Instead, it uses “now” via _sinceEpoch(Time.timestamp()), so it returns the current period regardless of the argument. Integrators relying on it to schedule or visualize claim windows will compute the wrong period for historical timestamps.

Vulnerability Details

Component: FirelightVault.sol

Functions:

  • periodAtTimestamp(uint48 timestamp)

  • _sinceEpoch(uint48 epoch)

Root cause

In FirelightVault.sol, periodAtTimestamp(uint48 timestamp) delegates to _sinceEpoch(periodConfiguration.epoch) when forming the period number, but _sinceEpoch is defined as Time.timestamp() - epoch. That means the function completely discards the timestamp parameter and computes the period for the present (“now”), not for the caller-provided time.

  • periodAtTimestamp(uint48 timestamp) returns startingPeriod + _sinceEpoch(uint48 epoch) / duration. It uses “now” instead of (timestamp - epoch) / duration.

  • _sinceEpoch(uint48 epoch) returns Time.timestamp() - epoch.

dApps that query a past/future period get the current period number instead. When users act on that misinformation and call claimWithdraw(uint256 period) too early, the contract reverts (if (period >= currentPeriod()) revert InvalidPeriod();). Funds remain safe, but UX is broken and automations fail.

Impact Details

  • Incorrect Scheduling: Any integrator using periodAtTimestamp() to map timestamps and period numbers will display/schedule the wrong claim period for historical timestamps. Users attempting to claim based on those schedules will encounter InvalidPeriod / NoWithdrawalAmount until the real period arrives.

  • Automation Churn: Off-chain bots may repeatedly trigger failed transactions (gas waste & alert noise).

Risk Breakdown

Low: Incorrect read causing disrupted UX without financial impact. This is a low-impact, high-likelihood issue.

Recommendation

Use the provided timestamp when computing the period offset from the configuration epoch:

No changes to currentPeriod*() functions are needed since they correctly use Time.timestamp() for “now.”

Proof of Concept

Proof of Concept

A deterministic Hardhat test shows periodAtTimestamp(tsPast) returns the current period rather than the past period because the implementation ignores its timestamp parameter.

Test Details:

  • Setup: Deploy the vault (initialize with a valid period configuration).

  • Choose a historical timestamp: historicalTs = epoch + duration + 10.

  • Advance time: Move forward several periods so currentPeriod() > expectedPast.

  • Observe:

    • periodAtTimestamp(historicalTs) equals currentPeriod() (bug).

    • The correct past period is starting + (historicalTs - epoch) / duration.

How to run:

Hardhat Test File: test/PoC_FirelightVault.periodAtTimestamp.spec.js

Was this helpful?