handleRandomness doesn't properly account for the current streak which could result in the User spinning losing funds from the Jackpot.
Vulnerability Details
Root cause: Wrong check, which is not inclusive of the current streak.
The flow for Spin.sol is described here: https://github.com/plumenetwork/contracts/blob/fe67a98fa4344520c5ff2ac9293f5d9601963983/plume/SPIN.md
When the SupraOracle callback processes handleRandomness, the contract tracks the user's streak (consecutive days they called startSpin()). Per docs and implementation, the user must have a streak of currentWeek + 2 to win the Jackpot:
Quote:
Streak Requirement: The user's streakCount must be greater than or equal to currentWeek + 2. This means the required streak to be eligible for the jackpot increases as the campaign progresses. If this check fails, the reward also defaults to "Nothing".
Relevant code excerpts:
When processing a request, the contract computes the current streak via _computeStreak:
Notes:
_computeStreak() factors the new streak correctly (it uses justSpun ? 1 : 0) and the returned currentSpinStreak is used in determineReward to pick the reward category (see A.1 and A.2).
However, the Jackpot eligibility check uses the stored userDataStorage.streakCount (pre-update) in the else if (userDataStorage.streakCount < (currentWeek + 2)) branch (A.3).
The stored streakCount is only updated later with userDataStorage.streakCount = currentSpinStreak; (A.4).
Because determineReward can return "Jackpot" based on the new streak but the subsequent eligibility check uses the old streak, the flow may fall into the "NotEnoughStreak" branch and convert a Jackpot to "Nothing" even when the user actually met the streak requirement for that spin.
Impact Details
Jackpot would be lost even if the user meets the criteria for streak. This loss is irreversible for that spin because the RNG outcome is consumed; future spins receive new RNG and there's no guarantee of hitting the Jackpot again. Thus unclaimed Jackpot value could be lost.
User calls startSpin() on the 4th day. _computeStreak will return 4 (it accounts for the current spin). determineReward(randomness, currentSpinStreak) uses this corrected streak and may return ("Jackpot", amount).
4
Step 4
But the Jackpot eligibility check uses userDataStorage.streakCount (still 3) and compares to currentWeek + 2 (4). The check fails, the reward is downgraded to "Nothing", and later userDataStorage.streakCount is updated to 4. The user thus loses the Jackpot they should have received.
function _computeStreak(address user, uint256 nowTs, bool justSpun) internal view returns (uint256) {
// if a user just spun, we need to increment the streak its a new day or a broken streak
uint256 streakAdjustment = justSpun ? 1 : 0;
...
if (today == lastDaySpun + 1) {
return userData[user].streakCount + streakAdjustment;
} // streak not broken yet
...
}