Reproduced Exploit
Bearcave — L2 never learns about new sleepers, so 3 prizes stick
addToParty pushes new sleeper ids on L1 and does not send a message that would push them on L2. wakeSleeper on L2 reverts once numUsed equals the stale sleepoors.length.
Chain
Other
Category
logic
Date
Jun 2023
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: 20609-c-01-state-is-not-properly-updated-on-l2-leading-to-stuck-pr. Standalone Foundry PoC and full write-up: 20609-c-01-state-is-not-properly-updated-on-l2-leading-to-stuck-pr_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/logic/state-update · vuln/logic/missing-check · vuln/dos/frozen-funds
Reproduction: a self-contained Foundry PoC that compiles and runs offline with only
forge-std— no fork and no RPC. Trace: output.txt. PoC: test/20609-c-01-state-is-not-properly-updated-on-l2-leading-to-stuck-pr_exp.sol.
Key info#
| Impact | HIGH — N = 3 prize NFTs, worth 300 PRIZE, cannot be woken |
| Protocol | Bearcave HibernationDen |
| Vulnerable code | wakeSleeper stops at party.sleepoors.length |
| Bug class | addToParty updates L1 and never messages L2 |
| Finding | Pashov Audit Group — BearCave, 1 June 2023 · C-01 · id 20609 |
| Report | 2023-06-01-BearCave |
| Source | AuditVault finding |
| Status | Reproduced on one chain as the L2 den. No second chain. |
| Compiler | solc 0.8.27, cancun, via IR |
TL;DR#
addToParty pushes new sleeper ids on L1 and does not send a message that would push them on L2. wakeSleeper on L2 reverts once numUsed equals the stale sleepoors.length.
The L2 party was synced with 7 sleepers. Three more prize NFTs are held by the den. After the 7 are paid, the 8th wake reverts PartyAlreadyWoke. N = 3 NFTs stay owned by the den and 300 PRIZE stays with them.
Mirroring the three ids onto the L2 array lets all 10 wakes succeed and pays 1,000 PRIZE.
The vulnerable code#
function wakeSleeper(uint256 bundleId_) external {
Party storage party = parties[bundleId_];
// @> VULN: L2 length was never updated, so numUsed == length bricks the rest
if (party.numUsed == party.sleepoors.length) revert PartyAlreadyWoke(bundleId_);
uint256 id = party.sleepoors[party.numUsed];
party.numUsed += 1;
// pay the prize and move the NFT
}
The report quotes that exact check. addToParty on L1 only does sleepoors.push.
Root cause#
The L2 length is a copy of the party. addToParty changes the L1 copy and does not change the L2 copy. Every later calculation that trusts party.sleepoors.length on L2, including the "already woke" guard, is short by the sleepers that were added after the last message.
Preconditions#
- L2 was synced when the party had 7 sleepers.
- Three more prize NFTs are in the den's custody, each with 100 PRIZE.
addToPartyhas recorded those three ids on L1 only.
Attack walkthrough#
- Seed L2 with ids 1 through 7.
- Mint ids 8, 9 and 10 to the L2 den and fund 1,000 PRIZE.
- Call
addToPartyon L1 with those three ids. L1 length becomes 3. L2 length stays 7. wakeSleepersucceeds 7 times and pays 700 PRIZE.- The 8th call reverts. Ids 8, 9 and 10 are still owned by the den. 300 PRIZE is left.
Control: mirror pushes the same three ids onto L2. Ten wakes pay 1,000 PRIZE and the den's balance is 0.
Diagrams#
Impact#
The three late sleepers cannot be claimed. 300 PRIZE and the three NFTs remain in the den. Every sleeper added after the last L2 sync is stuck the same way.
Remediation#
addToParty must send the cross-chain message that pushes the same ids into party.sleepoors on L2 before anyone relies on that length.
Sources#
- AuditVault finding 20609
- Pashov BearCave review, 1 June 2023
- Quoted check from that report. The L2 den in this file is the local model of
HibernationDen.wakeSleeper. The Honey Jar contracts lived in0xHoneyJar/honeyjar-contracts.
Taxonomy#
vuln/logic/state-update·vuln/logic/missing-check·vuln/dos/frozen-funds
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 20609-c-01-state-is-not-properly-updated-on-l2-leading-to-stuck-pr_exp (evm-hack-registry mirror).
- AuditVault finding: 20609-c-01-state-is-not-properly-updated-on-l2-leading-to-stuck-pr.
Alerts & third-party analyses
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.