# Changes to Pyth entropy provider used by ScaledEntropyProvider allow attacker to fix jackpot result

ScaledEntropyProvider keys pending requests only by Pyth sequence number, so after an entropy provider swap an attacker pre-seeds a fixed outcome and wins the jackpot.

- Payout: Contest reward
- Severity: medium
- Program: Megapot
- Credited: hgrano
- Disclosed: 2026-01-05
- Write-up quality: 8/10
- Original: https://code4rena.com/reports/2025-11-megapot#m-06
- Library page: https://bugbountylibrary.com/r/c4%3A2025-11-megapot%3AM-06
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:chained technique:source-review vuln:business-logic vuln:oracle-manipulation

**Target:** ScaledEntropyProvider / Jackpot lottery contract on Base
**Root cause:** Pending requests are stored in a mapping keyed only by the Pyth sequence number, but sequence numbers are per-provider; after setEntropyProvider swaps providers, a stale pending entry from the old provider collides with a new request at the same sequence.
**Steps:**
1. Attacker front-runs admin's `setEntropyProvider` by calling `requestAndCallbackScaledRandomness` with `_requests` = [{samples:5,minRange:1,maxRange:5,withReplacement:false},{samples:1,minRange:1,maxRange:1}] and a callback that reverts, so `pending[s]` is never cleared.
2. Admin calls `setEntropyProvider(newProvider)` where newProvider's sequence < s.
3. Attacker buys tickets matching the deterministic outcome (normals 1-5, bonus 1).
4. Attacker calls `Entropy::requestV2(newProvider)` repeatedly until its sequence reaches s-1.
5. In the same tx, attacker calls `Jackpot::runJackpot`, overwriting `pending[s].callback/selector/context` while attacker's `setRequests` remain at indices 0-1.
6. New provider reveals; `scaledEntropyCallback` uses attacker's fixed numbers; attacker claims winnings.
**How found:** Code review of `_storePendingRequest` noticed the mapping is keyed only by `sequence` while Pyth tracks sequence per provider address; hypothesized collision across provider swaps and built a forked-mainnet PoC.
**Exploitation:** Pre-seed a colliding pending entry with a reverting callback, wait for admin provider swap, advance the new provider's sequence to the collision point, buy matching tickets, trigger runJackpot in the same tx as the final requestV2, then claim the jackpot. PoC shows ~166,000 USDC profit.
**Escalation:** If new provider's sequence is higher, attacker can instead spam requestV2 on the old provider to push its sequence past the new one, or wait for natural sequence catch-up.
**Insight:** When integrating an external oracle/VRF that returns a per-provider monotonic counter, never key internal state by that counter alone—include the provider address, since admin-swappable providers reset or diverge counters and enable stale-entry collisions.
