Line 35 declares the immutable field:
But the constructor (lines 67-75) NEVER initializes it:
In Solidity, uninitialized immutable fields default to 0x0000...0000.
The double-claim check at line 88 becomes if (claimed[0x0000...0000]) revert —
this checks if 0x0 is claimed, but 0x0 is never marked as claimed.
Meanwhile, actual treasures ARE marked as claimed, but the check bypasses them.
This creates state inconsistency:
claimed[treasureHash] = true (marked correctly)
claimed[_treasureHash] = always false (check uses this wrong key)
Likelihood:
Triggered on every single claim() call from deployment
No special conditions needed — uninitialized immutable is permanent
Impact:
Double-claim check is permanently bypassed
Same treasure can be claimed unlimited times
All 100 ETH in the contract is at risk of full drainage
This test proves the state inconsistency. After a treasure is claimed,
claimed[treasureHash] is correctly true, but claimed[bytes32(0)]
which is what line 88 actually checks — remains false, so the next
claim for the same treasure still succeeds.
The field is fundamentally broken — never initialized, never set, and
only used in the broken check. Deleting it is the cleanest fix.
Option 2 would be to initialize it in the constructor, but the field
has no valid purpose so deletion is recommended.
In `claim()`, the guard uses `claimed[_treasureHash]`, where `_treasureHash` is an immutable state variable that is never initialized to the caller-supplied treasure identifier, while the contract later marks `claimed[treasureHash] = true` using the function argument instead. As a result, the duplicate-claim check and the state update are performed against different keys, which means a previously claimed treasure is not actually blocked from being claimed again with the same valid proof and `treasureHash`. This breaks a core invariant of the protocol described in the README, namely, that each treasure can only be redeemed once, and allows one valid treasure/proof pair to be reused to drain rewards repeatedly until either the `MAX_TREASURES` cap or the contract balance is exhausted.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.