52986 sc high jackpot check uses previous streakcount instead of current computed streak denying jackpot on first eligible day

Submitted on Aug 14th 2025 at 15:10:59 UTC by @daxun for Attackathon | Plume Network

  • Report ID: #52986

  • Report Type: Smart Contract

  • Report severity: High

  • 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

Description

Brief / Intro

During a spin, the contract computes:

  • currentSpinStreak = _computeStreak(user, now, true) (which includes today’s spin)

but verifies jackpot eligibility against the stored userData[user].streakCount (yesterday’s value). On the first day a user reaches the required streak, they can be downgraded to “Nothing”.

Vulnerability Details

In handleRandomness() (simplified):

handleRandomness (simplified)
uint256 currentSpinStreak = _computeStreak(user, block.timestamp, true);
...
if (isJackpot) {
    uint256 currentWeek = getCurrentWeek();
    if (currentWeek == lastJackpotClaimWeek) { ... }
    else if (userDataStorage.streakCount < (currentWeek + 2)) {
        // ↓ uses previous stored streak instead of currentSpinStreak
        rewardCategory = "Nothing"; rewardAmount = 0;
    } else {
        // award jackpot
    }
}
...
userDataStorage.streakCount = currentSpinStreak; // updated only after the check

Because userDataStorage.streakCount is updated after the check, a user who just reached the required streak today is unfairly denied.

Impact Details

  • Immunefi Impact: Low — Contract fails to deliver promised returns, but doesn't lose value

  • Users meeting the milestone can miss the jackpot exactly on the day it should be awarded.

Proof of Concept

1

Scenario setup

Assume requiredStreak = currentWeek + 2 = 5.

2

Yesterday's stored streak

Yesterday’s stored userData[user].streakCount == 4 (consecutive).

3

Today's computed streak

Today, _computeStreak(..., true) returns 5.

4

Jackpot check uses stored value

Jackpot path checks userDataStorage.streakCount < 5true, so it sets reward to “Nothing”.

5

streakCount updated after check

Only after the check, it writes userDataStorage.streakCount = 5.

6

Net result

Net: user is denied the jackpot today, despite meeting the requirement.

References

  • handleRandomness() source: https://github.com/immunefi-team/attackathon-plume-network/blob/580cc6d61b08a728bd98f11b9a2140b84f41c802/plume/src/spin/Spin.sol#L207-L265

Was this helpful?