SNARKeling Treasure Hunt

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

L-01: recipient == msg.sender check blocks legitimate self-payment without any security benefit

Author Revealed upon completion

Root + Impact

Description

  • claim() validates the recipient address against several invalid cases. The ZK circuit binds the recipient into the proof as a public input, which already prevents a front-runner from redirecting a stolen proof to themselves. The verifier would reject any proof where the submitted recipient differs from the one committed in the circuit.

  • Despite this cryptographic protection, claim() additionally rejects any call where recipient == msg.sender, forcing every participant to use a second address to receive their own reward.

function claim(bytes calldata proof, bytes32 treasureHash, address payable recipient) external nonReentrant() {
...
// @> recipient == msg.sender blocked — no cryptographic justification given circuit binding
if (recipient == address(0) || recipient == address(this) || recipient == owner || recipient == msg.sender)
revert InvalidRecipient();
...
}

Risk

Likelihood:

  • Affects every participant who tries to claim their own reward at the submitting address a natural and common pattern.

Impact:

  • Participants who control only one address cannot claim their reward without setting up a second wallet.

  • Creates unnecessary friction that may result in legitimate finders being unable to claim before the hunt ends.

Proof of Concept

A participant who controls only one wallet address and generates a proof with their own address as the recipient cannot claim their reward. The circuit binds the recipient into the proof, so they cannot simply change the recipient to a second address after the fact a new proof would need to be generated. This forces every single-wallet participant into a multi-step workaround to collect a reward they legitimately earned.

// Participant generates a valid proof with recipient = their own address
// and submits from that same address.
vm.prank(participant);
hunt.claim(proof, treasureHash, payable(participant));
// Reverts with InvalidRecipient — despite the proof being valid

Recommended Mitigation

The recipient == msg.sender condition provides no security value because the ZK circuit already cryptographically binds the recipient at proof-generation time — a third party cannot redirect the reward even without this check. Removing the condition restores the natural and expected behavior (paying the submitter directly) without weakening any security property. The other three conditions (address(0), address(this), owner) guard against genuinely dangerous recipient values and should be retained.

- if (recipient == address(0) || recipient == address(this) || recipient == owner || recipient == msg.sender)
+ if (recipient == address(0) || recipient == address(this) || recipient == owner)
revert InvalidRecipient();

Support

FAQs

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

Give us feedback!