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

50487 sc low cross campaign jackpot denial due to state pollution

Submitted on Jul 25th 2025 at 10:19:28 UTC by @IronsideSec for Attackathon | Plume Network

  • Report ID: #50487

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts: Theft of unclaimed yield

Description

Brief / Intro

The Spin.sol contract suffers from a state pollution vulnerability where lastJackpotClaimWeek is not reset when a new campaign starts via setCampaignStartDate(). This can cause legitimate users in subsequent campaigns to be denied their jackpot prizes if they win a jackpot in a week number that was already used by a previous campaign's jackpot winner. Losses can be up to 100,000 PLUME tokens per affected user (final week 12 jackpot).

See the Recommended Fix below for an easy mitigation.

Vulnerability Details

1

Campaign 1

User A wins a jackpot in week 5 → lastJackpotClaimWeek = 5. No further jackpots are won until campaign end.

2

Campaign Transition

Admin calls setCampaignStartDate(newDate)campaignStartDate updates but lastJackpotClaimWeek remains 5 (stale).

3

Campaign 2

During Campaign 2, no jackpots for first 4 weeks. On week 5, User B legitimately wins:

  • getCurrentWeek() = 5

  • currentWeek == lastJackpotClaimWeek → jackpot denied (reward becomes "Nothing")

The proof-of-concept test demonstrates this scenario:

  • Campaign 1 jackpot in week 5: ✅ 20,000 PLUME awarded

  • Campaign 2 jackpot in week 5: ❌ 0 PLUME awarded (denied)

Root Cause - Incomplete State Reset

The admin function that starts a new campaign updates campaignStartDate but does not reset lastJackpotClaimWeek, leaving stale state that is used by the jackpot validation logic.

Vulnerable setter:

Vulnerable jackpot validation:

Week calculation:

Impact Details

  • Direct per-user loss: up to 100,000 PLUME tokens (week 11/12 jackpot).

  • Affected users: users with long streaks (13+ day streaks) who legitimately earn jackpots.

  • Spin fee loss: 2 PLUME per denied spin (no refund mechanism).

  • Systemic: weeks where jackpots were previously claimed become blocked across campaigns if the same week number occurs again; this effectively lets earlier campaigns "reserve" jackpot weeks for future campaigns, reducing availability and harming high-value users disproportionately.

Severity multipliers:

  • Users cannot control or predict this.

  • Affects legitimately earned jackpots.

  • No recovery mechanism for denied jackpots.

  • Cumulative across campaigns.

Reset lastJackpotClaimWeek when starting a new campaign so prior campaign data cannot block jackpot claims in the new campaign.

Suggested change:

(Use an appropriate sentinel value consistent with the contract's initialization behavior — e.g., a very large number or the original initial value used in contract deployment.)

Proof of Concept

Gist with PoC and test steps: https://gist.github.com/IronsideSec/107fd6c7c13961071c2a7bf52de339d2

Steps (reproduced from the Gist) shown as a stepper:

1

User1 spins daily for 7 days in Campaign 1 to build streak.

2

User1 hits jackpot in week 5 of Campaign 1 → lastJackpotClaimWeek = 5. Campaign 1 ends.

3

Admin starts Campaign 2 with setCampaignStartDate() → BUG: lastJackpotClaimWeek still = 5.

4

User2 spins daily for 7 days in Campaign 2 to build streak.

5

User2 hits jackpot in week 5 of Campaign 2 (winning RNG).

6

Contract checks: currentWeek (5) == lastJackpotClaimWeek (5) → TRUE.

7

Contract emits JackpotAlreadyClaimed and converts reward to "Nothing".

8

User2 loses the 2 PLUME spin fee and gets 0 payout despite winning RNG.

9

Fix: Reset lastJackpotClaimWeek in setCampaignStartDate() function.

References

https://gist.github.com/IronsideSec/107fd6c7c13961071c2a7bf52de339d2

Was this helpful?