59635 sc low timestamp agnostic periodattimestamp misreports historical periods breaking time locked logic

Submitted on Nov 14th 2025 at 10:18:46 UTC by @vivekd for Audit Comp | Firelight

  • Report ID: #59635

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts:

    • Contract fails to deliver promised returns, but doesn't lose value

Description

Brief/Intro

periodAtTimestamp(uint48) is supposed to tell integrators which period corresponds to any arbitrary timestamp. The function instead always returns the current period because it ignores its timestamp argument, so anything relying on historical or future lookups receives incorrect data. This can misconfigure time-locked withdrawals, scheduled upgrades, or analytics built atop the vault.

Vulnerability Details

  • periodAtTimestamp fetches the correct PeriodConfiguration for the supplied timestamp (contracts/FirelightVault.sol:246-248), but then calls _sinceEpoch(periodConfiguration.epoch).

  • _sinceEpoch unconditionally returns Time.timestamp() - epoch, i.e., "now minus epoch," with no way to pass the caller's timestamp (contracts/FirelightVault.sol:795-797).

  • Therefore periodAtTimestamp(x) collapses to startingPeriod + (now - epoch) / duration, which is simply currentPeriod() regardless of x.

  • Functions such as currentPeriod() and analytics that depend on this view are fine, but anyone trying to reason about historical or future periods using the public getter gets incorrect data.

Impact Details

Off-chain systems (withdrawal schedulers, upgrade planners, risk dashboards) cannot safely reason about periods that are not "right now," which can lead to incorrect unlock times or reporting.

Proof of Concept

Proof of Concept

What the code does

Deploys the vault, records the epoch of the first period (time zero), advances three periods, then compares currentPeriod() with periodAtTimestamp(genesisEpoch)—a query that should have returned 0. The test asserts they're equal, showing the getter ignores its argument.

How to run

  1. Install deps if needed: npm install

  2. Ensure .env is populated (any dummy 32-byte keys).

  3. Run only this test: npx hardhat test test/period_timestamp_poc.js

Logs

The PoC reproduces the bug: querying the genesis timestamp yields the current period instead of 0.

Was this helpful?