The immutable state variable _treasureHash is declared but never initialized in the constructor (or anywhere else). The claim() function checks claimed[_treasureHash] instead of the treasureHash parameter that the caller actually supplied.
The AlreadyClaimed protection is completely bypassed for every call. Any treasure can be claimed multiple times (or the check always fails), allowing users or attackers to drain more REWARD funds from the contract than intended.
Normal Behavior: The claim() function is intended to allow any user to claim a unique treasure/reward by supplying a treasureHash (along with a valid proof). Before paying out the REWARD, the contract must check the claimed mapping using the caller-provided treasureHash to ensure that the same treasure has not already been claimed.
Specific Issue: Because the immutable state variable _treasureHash is declared but never initialized, the AlreadyClaimed check on line 88 (and the duplicate line inside the function) always reads from claimed[_treasureHash] instead of the treasureHash parameter that was passed in. This makes the duplicate-claim protection completely ineffective.
Likelihood:
The bug is present in every deployment of the contract because _treasureHash is declared as immutable but is never assigned a value in the constructor or anywhere else in the codebase.
Every single call to claim() executes the broken AlreadyClaimed check using the uninitialized _treasureHash slot instead of the supplied treasureHash parameter.
Impact:
Any user can claim the same treasure (or any treasure) an unlimited number of times.
The contract will pay out the full REWARD amount on every successful call, allowing the entire contract balance to be drained far beyond the intended MAX_TREASURES limit.
This PoC demonstrates that the AlreadyClaimed protection is completely ineffective. The check always uses the uninitialized _treasureHash (which is always bytes32(0)) instead of the caller-supplied treasureHash. As a result, the same treasure can be claimed repeatedly, bypassing the intended double-spend protection.
Replace the incorrect check that uses the uninitialized immutable variable with a check against the treasureHash parameter supplied by the caller.
There is also a test in TreasureHunt.t.sol called testClaimDoubleSpendReverts that has a bug. On line 144 there is a vm.expectRevert(); call that is commented out. This is wrong because hunt.claim() being called a second time should revert.
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.
The contest is complete and the rewards are being distributed.