Deploy.s.sol comments the 10 advertised treasure hashes that are meant to be the public claimable set. The Noir circuit's baked-in ALLOWED_TREASURE_HASHES array — the allow-list the ZK circuit proves against — does not match that set. The advertised ninth treasure hash is absent from the circuit, and the tenth treasure's hash appears twice. As a result, no finder of the ninth advertised treasure can produce a valid proof for it, and the hunt's normal completion path (claimsCount >= MAX_TREASURES in withdraw()) is unreachable by legitimate unique claims once the replay bug in claim() is patched.
Advertised inventory in contracts/scripts/Deploy.s.sol#L16-L26:
Circuit allow-list in circuits/src/main.nr#L54-L66:
The ninth advertised hash (-4417726114...1452385022043124552) is not present anywhere in the circuit. Treasure 10's hash appears twice, at indices 8 and 9.
Likelihood: Medium.
The misconfiguration is deterministic; every deployed instance of the hunt carries it.
It only manifests when a participant finds treasure 9 and tries to prove it. Any other discovery still produces a valid proof, so the bug is silent unless treasure 9 is found.
Impact: Medium.
The ninth reward slot (10 ETH) is permanently unclaimable through the legitimate flow; the circuit rejects any proof that binds that treasure hash to a recipient.
MAX_TREASURES cannot be reached via legitimate unique claims, which blocks withdraw()'s post-hunt exit condition and leaves residual funding dependent on the owner's emergency withdrawal path.
No funds are directly stolen; the failure mode is degraded availability plus forced reliance on a trusted emergency flow.
Foundry test asserts the exact mismatch between the advertised inventory and the circuit allow-list. File: contracts/test/hunter_pocs/CircuitHashSetPoC.t.sol.
Command + result:
Replace ALLOWED_TREASURE_HASHES[8] in circuits/src/main.nr with the advertised ninth hash, regenerate the Noir circuit + the HonkVerifier Solidity contract + contracts/test/fixtures/proof.bin, and add a circuit test that iterates the full [1,2,3,4,5,6,7,8,9,10] treasure list instead of the current [1,2,3,4,5,6,7,8,10,10].
The issue stems from a mismatch between the circuit and the contract’s economic assumptions: the Solidity contract is configured for `MAX_TREASURES = 10` and only allows the owner to call `withdraw()` once `claimsCount >= MAX_TREASURES`, while the Noir circuit’s baked-in `ALLOWED_TREASURE_HASHES` array does not actually contain ten distinct treasures because one hash is duplicated and another expected hash is missing. As a result, under the intended one-claim-per-treasure design described in the README, there are only nine uniquely claimable treasures even though the system is funded and accounted as if ten rewards can be legitimately redeemed. That creates two linked consequences from the same root cause: first, one treasure is effectively unclaimable because no valid proof can ever be generated for the missing allowed hash, and second, the normal “hunt over” withdrawal path becomes bricked because honest participants can never reach ten legitimate unique claims, leaving the post-hunt fund recovery logic via `withdraw` function permanently unreachable. The owner can still intervene through the emergency path.
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.