For the complete documentation index, see llms.txt. This page is also available as Markdown.

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?