The claim() function relies on a zk-SNARK proof that includes recipient as a public input. However, the Noir circuit does not use or constrain this value, meaning it is not bound to the validity of the proof. As a result, an attacker can front-run a valid transaction by modifying the recipient field while keeping the same proof, allowing them to redirect the reward.
In the Noir circuit:
The recipient public input is declared but never used in any constraint. This means it is not enforced by the zk proof and can be arbitrarily modified without invalidating it.
In zk-SNARK systems, unconstrained public inputs are effectively free values: they are included in the verification process but do not influence proof validity.
Because recipient is not constrained, an attacker can:
Observe a pending claim() transaction in the mempool
Copy the valid proof and treasureHash
Replace recipient with their own address
Submit the transaction with higher gas to front-run the original
Even though the proof remains valid, the reward is sent to the attacker because the contract trusts the unconstrained public input.
Enables front-running of valid claims
Allows redirection of rewards without breaking proof validity
Breaks the assumption that all public inputs are cryptographically bound to the proof
Creates MEV extraction opportunities at user expense
High
Justification:
Proof remains valid under modified public inputs
Enables deterministic front-running
Directly impacts reward integrity
The root issue is that recipient is exposed as a public input but is not constrained in the Noir circuit. The correct fix is to ensure that all public inputs are explicitly bound inside the zk circuit, so they cannot be modified without invalidating the proof.
Ensure recipient is properly constrained in the circuit :
The claim that the proof system is broken because the recipient is not explicitly constrained in the Noir circuit reflects a misunderstanding of how zero-knowledge proofs bind public inputs. Although the circuit does not impose algebraic constraints on recipient, the value is still included in the public input vector, which is cryptographically committed to during proof generation. As a result, the proof is only valid for the exact tuple of public inputs it was created with. Any attempt by an attacker to front-run and substitute a different recipient would alter this tuple, causing the verifier’s check to fail because the proof no longer matches the provided public inputs. Therefore, while unconstrained public inputs do not enforce logical relationships within the circuit, they remain inseparably bound to the proof itself, and this binding is sufficient to prevent tampering or replay with modified values. Run the unit tests 'testClaimInvalidProofFails', 'testFrontRunningClaimFails'.
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.