51776 sc low streak system breaks despite timely user action due to delayed supra oracle callback

  • Submitted on Aug 5th 2025 at 18:36:20 UTC by @light279 for Attackathon | Plume Network

  • Report ID: #51776

  • Report Type: Smart Contract

  • Report severity: Low

  • Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/plume/src/spin/Spin.sol

  • Impacts:

    • Temporary freezing of funds for at least 24 hours

    • Protocol insolvency

Description

Brief/Intro

The contract allows users to spin once per day and maintains a streak system to reward consecutive daily spins. However, the streak computation relies on block.timestamp inside the Spin::handleRandomness callback, which is triggered asynchronously by the Supra Oracle. This introduces a time gap between the user-initiated Spin::startSpin and the actual randomness handling, potentially breaking user streaks even if the user called the function before the end of the day.

Vulnerability Details

The function _computeStreak calculates whether a user’s spin continues their streak based on the current block timestamp during the handleRandomness execution. But since startSpin only emits a randomness request and the actual response is handled asynchronously (likely in a different block and time), a delay in the Supra oracle’s callback may push the effective time of handleRandomness into the next day.

This results in nowTs / SECONDS_PER_DAY != lastSpinTs / SECONDS_PER_DAY + 1 even when the user called startSpin on time. The streak is then reset despite correct user behavior.

Code excerpt of _computeStreak:

This issue manifests in handleRandomness where block.timestamp (the oracle callback time) is used instead of the user's original spin time:

Using block.timestamp here does not reflect the user's original spin time, but the time at which the oracle responds.

Impact Details

Proof of Concept

1

Scenario setup

Assume current day is Day X. A user spins at 23:59 (end of Day X) by calling startSpin().

2

Oracle latency occurs

startSpin() correctly generates a Supra oracle request and passes callback signature handleRandomness(uint256,uint256[]). Due to Oracle latency, the callback handleRandomness() is only called at 00:00:10 on Day X+1.

3

Streak calculation at callback

In handleRandomness(), the _computeStreak() function is called with block.timestamp = 00:00:10, which translates to Day X+1. _computeStreak() checks:

But today = X+1, lastDaySpun = X-1, so the condition fails.

4

Result

The fallback clause is executed:

So the user's streak is reset even though they spun on Day X. This causes unexpected behavior and harms streak-based rewards.

Was this helpful?