# Draws can be retried even if a random number is available or the current draw has finished

RngWitnet.isRequestFailed() contradicts isRequestComplete(), letting draw bots retry startDraw() after randomness exists or draw finished, diluting or wasting bot rewards.

- Payout: Contest reward
- Severity: medium
- Program: PoolTogether
- Credited: MiloTruck
- Disclosed: 2024-05-10
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2024-05-pooltogether-judging/issues/27
- Library page: https://bugbountylibrary.com/r/sherlock%3A2024-05-pooltogether%3AM-1
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:solidity technique:source-review vuln:business-logic vuln:oracle-manipulation vuln:reward-distribution

**Target:** RngWitnet oracle adapter / DrawManager.startDraw
**Root cause:** isRequestFailed() checks only the specific request's response status, while Witnet's isRandomized()/fetchRandomnessAfter() return success if any later request succeeded, so a request can be both 'failed' and 'complete'.
**Steps:**
1. Request 1 at block 100 fails; request 2 at block 101 succeeds.
2. isRequestComplete(1) returns true (isRandomized finds success at/after 100) and randomNumber() returns valid value.
3. isRequestFailed(1) also returns true since request 1's status is Error.
4. DrawManager.startDraw() only reverts if !isRequestFailed(lastRequestId), so it passes and a new request is submitted.
5. Alternatively: finishDraw() succeeds via request 2, then startDraw() still passes because request 1 is 'failed'.
**How found:** Code-reading of RngWitnet.isRequestFailed() vs isRequestComplete()/randomNumber() and noticing the contradiction when a later request succeeds but an earlier one errored.
**Exploitation:** Draw bots call startDraw() to retry; an attacker front-runs with finishDraw() so the bot's startDraw() executes after the draw concluded, and the bot never receives rewards. In the other case, retries dilute rewards of bots that already called startDraw().
**Insight:** When an oracle adapter wraps a 'first success at or after block N' API, any per-request status check must be reconciled with that semantics; a request can be simultaneously failed and superseded, so guard conditions based on one view can be bypassed.
