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

51970 sc low spin streak computation relies on oracle callback time any third party delay can reset the user s streak and block jackpot eligibility

Submitted on Aug 6th 2025 at 22:12:19 UTC by @jovi for Attackathon | Plume Network

  • Report ID: #51970

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts:

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

    • Users lose value due to a timing edge case

Description

Brief / Intro

Spin.startSpin() records the spin request but defers streak accounting until the VRF callback. If the Supra DVRF oracle has a partial delay that makes it answer after midnight of the day the spin was requested, handleRandomness() treats the spin as belonging to the next day, so _computeStreak() declares the previous‑day streak “broken” — even though the user had made a randomness request on the day that would maintain the streak. Users permanently lose their accumulated streak, raffle‑multiplier, and the ability to win the weekly jackpot for a certain period.

Details

startSpin() stores isSpinPending[user] = true but does not touch lastSpinTimestamp. The function that updates the user streak, _computeStreak(), is called only when handling randomness during the Supra DVRF oracle callback. This function considers the streak unbroken only when today == lastDaySpun + 1. Because block.timestamp is evaluated when SupraRouter calls back and not when the spin request was made, any network / gas‑price congestion, operator throttling, or malicious delay that pushes the callback past the day boundary causes:

1

_computeStreak() returns 1 instead of prevStreak+1.

(Callback occurs after midnight so the computed "today" is too large relative to lastDaySpun.)

2

userData[user].streakCount is overwritten with the smaller value.

(The old streak is replaced on-chain when randomness is handled.)

3

Jackpot guard clauses revert the jackpot to "Nothing".

(A missed streak leads to ineligibility for the jackpot and reduced raffle multipliers.)

The user's old streak is lost.

Impact

  • Loss of value for honest users:

    • Jackpot prizes up to 100 000 PLUME can be lost for the victim.

    • Raffle‑ticket rewards are drastically reduced (baseRaffleMultiplier × streak).

  • DoS / griefing: An attacker who controls or economically influences the SupraRouter queue can batch many spin requests near midnight and hold the callback until after 00:00, wiping streaks for all pending players.

While no funds are stolen from the contract, the bug causes direct, involuntary loss of user rewards by breaking a spin streak.

Proof of Concept

1

Setup

  • Deploy Spin with campaign started.

  • User Alice has streakCount == 6 and lastSpinTimestamp set to 23:10 UTC of 14 Aug 2025.

2

Exploit

  • At 23:55 UTC on 15 Aug 2025 Alice calls startSpin(); lastSpinTimestamp is still 23:10 UTC (14 Aug).

  • A network congestion delays the callback until 00:05 UTC on 16 Aug 2025.

3

Observation

  • handleRandomness() now computes: today = 00:05/86400 = 19690 and lastDaySpun = 23:10/86400 = 19688, so today == lastDaySpun + 2; the streak is treated as broken → returns 1.

  • Alice’s 7‑day streak is lost; she receives zero jackpot if the rng had determined it; and reduced prizes in the other cases.

4

Result

  • Direct monetary loss had she hit the jackpot for that week plus diminished raffle / PP rewards.

Was this helpful?