52706 sc low multi quantity prize claims revert until all winners are drawn freezing early winners

Submitted on Aug 12th 2025 at 14:36:49 UTC by @wellbyt3 for Attackathon | Plume Network

  • Report ID: #52706

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts: Temporary freezing of funds for at least 1 hour

Description

Brief/Intro

claimPrize() reverts for multi-quantity prizes until all winners are drawn, so early winners (e.g., week 1 of a 4-week raffle) can’t claim until the final draw, temporarily freezing their prize.

Vulnerability Details

When a raffle participant wins a prize, they call claimPrize() to claim the prize they won.

However, this call will revert if the quantity of the prize is > 1 and all the winners haven't been drawn:

function claimPrize(uint256 prizeId, uint256 winnerIndex) external {
     if (prizes[prizeId].isActive && winnersDrawn[prizeId] < prizes[prizeId].quantity) {
            revert WinnerNotDrawn();
        }
...SNIP...
}

This causes an issue if the same prize is intended to be distributed (for example once a week for 4 weeks).

A user who wins the prize in week 1 won't be able to claim until after the week 4 prize has been drawn, temporarily freezing the prize they're entitled to.

Impact Details

Temporary freezing of funds (i.e., raffle prizes).

References

https://github.com/immunefi-team/attackathon-plume-network/blob/580cc6d61b08a728bd98f11b9a2140b84f41c802/plume/src/spin/Raffle.sol#L298-L301

Proof of Concept

Repro test (Forge)

Add a new .t.sol file to /plume/test/, paste the code below, then run:

forge test --mt test_userCannotClaimPrizeIfNotAllWinnersDrawn -vv

Reproduction Steps

1

Step 1 — Setup and spin

  • Fund user with test ETH and have them call spin.startSpin{value: 2 ether}().

  • Simulate randomness callback so the user acquires raffle tickets.

2

Step 2 — Add multi-quantity prize

  • Admin adds a prize with quantity = 2 via raffle.addPrize(...).

3

Step 3 — Spend raffle tickets

  • The user spends raffle tickets on the prize (raffle.spendRaffle(prizeId, amount)).

4

Step 4 — Request and draw first winner

  • Admin requests a winner for the prize (raffle.requestWinner(prizeId)).

  • Simulate the RNG callback and ensure the first winner is the user.

5

Step 5 — Attempt to claim

  • The user calls raffle.claimPrize(prizeId, winnerIndex) and the call reverts with WinnerNotDrawn because winnersDrawn[prizeId] < prizes[prizeId].quantity.

Notes

  • This is a logical bug that causes early winners of multi-quantity prizes to be unable to claim their prize until the final winner has been drawn (temporarily freezing the prize).

  • Severity assessed as Low, impact is temporary freezing of prizes until draws complete.

Was this helpful?