SNARKeling Treasure Hunt

First Flight #59
Beginner FriendlyGameFiFoundry
100 EXP
Submission Details
Impact: low
Likelihood: high

Broken View Function: `isClaimed` always returns `false` due to Hash Mismatch

Author Revealed upon completion

Root + Impact

Description

In a healthy contract, a view function should accurately reflect the state updated by the logic functions.

However, in this contract:

  1. The claim function contains a check: if (claimed[_treasureHash]) revert AlreadyClaimed(treasureHash);.

  2. As we discovered, _treasureHash is an uninitialized immutable (defaulting to 0x0).

  3. Because the claim function will likely revert or behave incorrectly due to this uninitialized variable, either no claims will ever be recorded, or they will be recorded under a logic that isClaimed cannot properly surface.

Furthermore, if a user queries isClaimed(treasureHash) for any real treasure, it will return false even if the contract is already "locked" or "drained" because the contract logic is looking at the wrong storage keys.

// Root cause in the codebase
function isClaimed(bytes32 treasureHash) external view returns (bool) {
@> return claimed[treasureHash]; // This queries the input hash...
}
// ...BUT the claim function checks a DIFFERENT variable:
if (claimed[_treasureHash]) revert AlreadyClaimed(treasureHash);

Risk

Likelihood: High

  • The state variable claimed is being used inconsistently across the contract.

Impact: Low

  • User Confusion: Users will see treasures marked as "Available" on the UI, but their transactions will revert when they try to claim them.

  • Frontend Failure: Any dashboard or map built for the snorkeling hunt will be permanently out of sync with the actual ability to claim rewards.

Proof of Concept

Recommended Mitigation

Ensure that the claim function consistently uses the treasureHash parameter for both checking and setting the claimed status.

function claim(bytes calldata proof, bytes32 treasureHash, address payable recipient) external nonReentrant() {
...
- if (claimed[_treasureHash]) revert AlreadyClaimed(treasureHash);
+ if (claimed[treasureHash]) revert AlreadyClaimed(treasureHash);
...
}

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!