Scope: contracts/src/TreasureHunt.sol, circuits/src/main.nr
TreasureHunt.claim(bytes proof, bytes32 treasureHash, address payable recipient) verifies the proof against public inputs (treasureHash, recipient) only. msg.sender is NOT part of the proof's public inputs, nor is it otherwise bound into the statement the ZK circuit proves.
Consequence: once Alice broadcasts a valid (proof, hash, Bob) transaction to the mempool, any third party Eve who observes it can copy the exact calldata verbatim and submit the same proof with a higher gas price from her own EOA. Eve's tx lands first; the contract:
Verifies the proof successfully (public inputs unchanged).
Distributes REWARD to Bob (the bound recipient — NOT to Eve).
In the post-fix world (i.e., after Finding 1's double-claim bug is patched), this marks the treasure as claimed, so Alice's subsequent tx reverts with AlreadyClaimed.
Note: under the CURRENT code, Finding 1 masks this — both tx's succeed because the duplicate-claim check reads claimed[_treasureHash] (always 0). So the front-run finding becomes materially relevant after Finding 1 is fixed, which any reviewer would presumably do.
Likelihood: MEDIUM (in the post-fix world)
Mempool sniping is a routine pattern; it only requires a public-chain mempool observer with non-trivial gas-budget tolerance.
Alice has no cryptographic or economic defense on this contract — she can't choose a msg.sender-bound proof because the circuit doesn't accept one.
Impact: LOW
Eve cannot steal ETH: the payout still goes to Bob (the recipient bound into the proof). Bob is typically under Alice's control, so her ECONOMIC position is unharmed.
Eve burns gas for herself to grief Alice, consuming the claim slot. This is a denial-of-service griefing vector: Alice wasted gas on a reverting tx, and Bob still gets paid, but Alice experiences a failed UX. Cumulative griefing across 10 claims increases the attacker's gas cost but does nothing beneficial for them.
In pathological cases (e.g., a finder who needs to claim quickly before the contract drains due to some other issue, or before the hunt's time window expires), griefing can cause the finder to lose their chance entirely.
Bind msg.sender to the proof's public inputs. This requires updating both the circuit and the contract, and regenerating the verifier fixtures:
This pins the proof to a specific claim-submitting address. Mempool sniping is no longer profitable because a sniped copy would fail verification (the submitter public input doesn't match Eve's msg.sender).
Alternatively, EIP-2930 / EIP-7702 account abstraction flows could be used so claims don't hit the public mempool at all.
This finding was identified and written up with the assistance of an autonomous AI auditor (Anthropic Claude) — surfaced during the Claude + GPT brainstorm review of the initial 6-finding batch. The finding is filed separately from Finding 1 because Finding 1's fix is prerequisite for this one to be materially exploitable on-chain.
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.