Reproduced Exploit
RipIt: When the first prizeId is selected
Chain
Other
Category
untagged
Date
Jan 1970
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 62544-h-07-spinlottery-will-break-if-the-first-prizeid-is-selected. Standalone Foundry PoC and full write-up: 62544-h-07-spinlottery-will-break-if-the-first-prizeid-is-selected_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/locked-funds
Reproduction: a faithful minimal reproduction of the vulnerable finding — the vulnerable function is reproduced verbatim (marked
@>) with faithful minimal doubles; local deploy, no fork.
Root cause#
When the first prizeId is selected, _claimPrize does both firstPrizeId++ and nextPrizeId--, collapsing a 2-prize window to 0 after one claim; the moved survivor and its escrowed NFT are permanently stranded (next spin reverts NoPrizesAvailable), vs the fixed variant which claims both prizes.
}
// If we've removed the first prize, increment firstPrizeId
if (prizeId == pool.firstPrizeId) {
pool.firstPrizeId++; // @> BUG: with nextPrizeId-- below, the window shrinks by 2 not 1; the moved survivor at the freed slot falls below firstPrizeId and is stranded forever
}
Why it's exploitable here#
When the first prizeId is selected, _claimPrize does both firstPrizeId++ and nextPrizeId--, collapsing a 2-prize window to 0 after one claim; the moved survivor and its escrowed NFT are permanently stranded (next spin reverts NoPrizesAvailable), vs the fixed variant which claims both prizes.
Attack path#
Marked-line walkthrough (Playground)#
The EVM Playground pins each step to the exact executed source line in 0x671d353a77…:
- L80 — Decode a packed prize slot: Helper
unpackPrizedecodes a packed slot into its NFT address,tokenId, and rarity — the accessor the claim path relies on. - L97 — Read the prize window pointers:
getPointersexposes a rarity'sfirstandnextpointers — the two indices whose gap defines how many prizes remain claimable. - L106 — Count claimable prizes:
claimableCountreports how many prizes sit between the first and next pointers in a rarity's window. - L122 — Enter the claim routine:
_claimPrizeselects a prize by random value and advances the window pointers to consume it. - L136 — Selected prize isn't the tail: Branch taken when the chosen
prizeIdis not the last slot, moving the tail survivor into the freed position. - L145 — First-slot claim shrinks window twice: Root-cause bug: when
prizeId == firstPrizeIdthe code both incrementsfirstPrizeIdand decrementsnextPrizeId, collapsing a 2-prize window to 0. - L153 — Unpack the won NFT: Unpacks the selected slot's packed value into the NFT address and
tokenIdto hand to the winner.
PoC#
Registry (Foundry, local deploy — verbatim vulnerable source + harm-asserting test + negative control):
cd 62544-h-07-spinlottery-will-break-if-the-first-prizeid-is-selected_exp
forge test -vvv
The browser Playground replays the same synthetic opcode-for-opcode and measures the harm: When the first prizeId is selected, _claimPrize does both firstPrizeId++ and nextPrizeId--, collapsing a 2-prize window to 0 after one claim. Both gates are green (registry forge test PASS + Playground _verify-poc VERDICT: PASS).
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 62544-h-07-spinlottery-will-break-if-the-first-prizeid-is-selected_exp (evm-hack-registry mirror).
- AuditVault finding: 62544-h-07-spinlottery-will-break-if-the-first-prizeid-is-selected.
- Upstream DeFiHackLabs PoC directory: src/test.
Alerts & third-party analyses
- DeFiHackLabs incident explorer: search "RipIt: When the first prizeId is selected".
- Web3Sec X hacked database: search.
- Rekt leaderboard: search.
- Solodit incident search: search.
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.