Scope: contracts/src/TreasureHunt.sol, contracts/scripts/Deploy.s.sol
The contract documents that it "should be funded with enough ETH to cover all rewards (default deployment flow uses 100 ether)" and the in-scope Deploy.s.sol defaults INITIAL_FUNDING to 100 ether. But the constructor itself accepts ANY msg.value, including zero:
If the deployer mis-sets INITIAL_FUNDING (e.g. forgets the env var, sets a wrong decimal count, or fat-fingers a small value), the contract deploys with insufficient ETH to pay all 10 rewards. Later claim() calls will succeed until the balance hits zero, then revert with NotEnoughFunds() — stranding any legitimate claimant whose slot happens to fall after the pool runs dry.
Also: since MAX_TREASURES is 10 but only 9 unique hashes are provable (Finding 2), the hunt cannot complete honestly. Combined with under-funding, this makes withdraw() even more unreachable.
Likelihood: MEDIUM
Requires owner mistake on deployment. Owner is trusted, but trust ≠ perfection; the constructor provides a cheap guardrail that this one doesn't use.
The README and comments set the expectation "100 ETH"; the code doesn't enforce it.
Impact: LOW
No direct theft. Later claimants DoS'd and some find-the-treasure work is stranded off-chain. Owner can reclaim unused ETH via emergencyWithdraw (requires pause), so no total-loss scenario.
Damages game UX / protocol reputation because discrepancies between "I have the secret" and "I got paid" look like a bug to the claimant.
(See testClaimWhenNotEnoughFundsFails in the existing test suite, which confirms this path reverts — demonstrating the failure mode without a guard at deployment.)
Enforce the funding invariant in the constructor:
(Or loosen to msg.value >= REWARD * MAX_TREASURES if overfunding is acceptable.)
Alternatively, if partial-funding deployments are a legitimate use case, add a corresponding DEPLOYED_UNDERFUNDED event so off-chain tooling surfaces the warning immediately.
This finding was identified with the assistance of an autonomous AI auditor (Anthropic Claude). Surfaced during the Claude + GPT brainstorm review of the initial 6-finding batch.
The liquidity-enforcement issue arises because the protocol assumes the hunt will be funded with enough ETH to cover all rewards, but the contract itself does not actively enforce that invariant at deployment time. The constructor accepts arbitrary `msg.value` and only validates the verifier address, even though the contract hardcodes a reward of 10 ether and a maximum of 10 treasures, implying an expected full funding target of 100 ether; the README likewise states that the contract is expected to be funded with enough ETH to cover all rewards and notes that the default deployment flow uses 100 ether. Although the deployment script sets `DEFAULT_INITIAL_FUNDING = 100 ether`, it also allows that amount to be overridden via `vm.envOr("INITIAL_FUNDING", DEFAULT_INITIAL_FUNDING)`, and the only post-deployment balance check is that the contract balance equals the chosen `initialFunding`, not that the chosen amount is actually sufficient to fund the full hunt. As a result, the system can be deployed in an underfunded state after which otherwise valid claims will begin reverting with `NotEnoughFunds()` once the balance drops below a single reward payment.
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.