claim() function is designed so that each unique treasureHash can only be claimed once. After a successful claim, the hash is recorded in the claimed mapping and any future attempt to claim the same hash should revert.The duplicate-claim guard reads from claimed[_treasureHash] — the private immutable — instead of claimed[treasureHash] — the caller-supplied argument. Because _treasureHash is never assigned in the constructor, it permanently holds bytes32(0). The guard therefore only ever blocks claims where the public input happens to be zero, leaving every real treasure hash unprotected. _markClaimed correctly writes to claimed[treasureHash], so the check and the write operate on permanently different storage keys.
function claim(bytes calldata proof, bytes32 treasureHash, address payable recipient) external nonReentrant() {
...
// @> reads from the uninitialized immutable, not the caller-supplied treasureHash
if (claimed[_treasureHash]) revert AlreadyClaimed(treasureHash);
...
_incrementClaimsCount();
// @> writes to the correct key — check and write are permanently mismatched
_markClaimed(treasureHash);
...
}
function _markClaimed(bytes32 treasureHash) internal {
// @> writes to claimed[treasureHash] — correct key, but the guard above never reads this
claimed[treasureHash] = true;
}
Likelihood: High
Any caller who obtains a valid ZK proof for a treasure can immediately resubmit the same proof and treasureHash in a second transaction, since the guard never trips for a non-zero hash.
The only natural rate-limiter is claimsCount >= MAX_TREASURES, meaning a single proof-holder can drain up to MAX_TREASURES × REWARD (100 ETH) before the contract halts.
Impact: Critical
A single valid proof can be replayed up to 10 times, draining the entire 100 ETH contract balance to a single recipient.
All other treasure hunters are permanently locked out — once claimsCount reaches MAX_TREASURES, AllTreasuresClaimed reverts every subsequent call, even for treasures that were never legitimately found.
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.