Reproduced Exploit
Y2K Earthquake — earlier rollover-queue users can grief later users (permanent rollover DoS)
Chain
Other
Category
dos
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: 18534-h-2-earlier-users-in-rollover-queue-can-grief-later-users-sh. Standalone Foundry PoC and full write-up: 18534-h-2-earlier-users-in-rollover-queue-can-grief-later-users-sh_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/dos/griefing · vuln/logic/state-desync
Reproduction: the test deploys the REAL audited
Carousel(which isVaultV2+ a standard OpenZeppelinERC1155) — no protocol logic is mocked. Only the opaque underlying and emissions tokens are minimal real ERC-20s. It fills the real rollover queue in the audited order and drives the realmintRollovers/delistInRolloverfunctions to permanently strand a later user's funds.
Root cause#
Audited source: sherlock-audit/2023-03-Y2K @ 93e3994, file
Earthquake/src/v2/Carousel/Carousel.sol,
vendored here at src/src/v2/Carousel/Carousel.sol.
mintRollovers(epochId, operations) tracks how far the queue has been processed with a
single index, rolloverAccounting[epochId], and starts each call at that index:
uint256 index = rolloverAccounting[_epochId]; // processed cursor
...
if (executions > 0) rolloverAccounting[_epochId] = index;
delistInRollover(owner) removes a queue entry with a swap-and-pop: it moves the
LAST queued user into the removed slot and shrinks the array:
rolloverQueue[index] = rolloverQueue[length - 1]; // last user overwrites removed slot
rolloverQueue.pop();
ownerToRollOverQueueIndex[rolloverQueue[index].receiver] = index + 1;
The cursor and the swap-and-pop are never reconciled. A user who has already been
processed (so the cursor sits above their index) can delist; the last, not-yet-
processed user is swapped below the cursor. The next mintRollovers begins at the
stale cursor, computes operations = length - index == 0, and silently skips the
shifted user — who can never be rolled over for that epoch.
The fix (Y2K PR #127) reworks the queue bookkeeping so a delist can no longer leave an unprocessable entry.
Reproduction (real numbers)#
relayerFee = 1e18, depositFee = 0. Alice and Bob each deposit 10e18 into epoch 1
and enlist for rollover, giving queue [Alice(idx0), Bob(idx1)]. Epoch 1 resolves as a
winner with claimTVL = 2 x finalTVL.
mintRollovers(2, 1)— Alice (index 0) is rolled into epoch 2 (mints10e18 - 1e18 = 9e18new-epoch shares);rolloverAccounting[2]advances to1.- Alice calls
delistInRollover(Alice)— swap-and-pop moves Bob into index 0 and pops the array to length1;rolloverAccounting[2]stays1. mintRollovers(2, 100)—index (1) == length (1)=>operations = 0; the loop never runs.
Harm: Bob ends with 0 epoch-2 shares, his rollover entry still points at the old
(resolved) epoch, and his 10e18 remain stranded in epoch 1 — the rollover core
functionality is denied until a brand-new epoch is created, despite Bob having correctly
enlisted.
Run#
_shared/run-poc/run_poc.sh 18534-h-2-earlier-users-in-rollover-queue-can-grief-later-users-sh_exp -vvvvv
Expected: [PASS] testEarlierUserGriefsLaterUserRollover. The assertions in
test/18534-h-2-earlier-users-in-rollover-queue-can-grief-later-users-sh_exp.sol
verify Bob's zero new-epoch shares, his stuck old-epoch rollover pointer, his stranded
10e18, and that the relayer cannot advance the cursor past him.
Sources#
- AuditVault finding #18534
- Sherlock 2023-03-Y2K issue #72
- Audited
Carousel.sol@ 93e3994 - Y2K fix PR #127
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 18534-h-2-earlier-users-in-rollover-queue-can-grief-later-users-sh_exp (evm-hack-registry mirror).
- AuditVault finding: 18534-h-2-earlier-users-in-rollover-queue-can-grief-later-users-sh.
- Upstream DeFiHackLabs PoC directory: src/test.
Alerts & third-party analyses
- DeFiHackLabs incident explorer: search "Y2K Earthquake".
- 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.