SNARKeling Treasure Hunt

First Flight #59
Beginner FriendlyGameFiFoundry
100 EXP
View results
Submission Details
Severity: medium
Valid

Unsafe Deployment Assumption on Initial Funding Can Lead to Immediate Claim DoS

Description

The deployment script assumes that the contract must always be funded with a fixed amount of 100 ether at deployment time:

uint256 constant DEFAULT_INITIAL_FUNDING = 100 ether;

However, this value is overrideable via an environment variable:

uint256 initialFunding = vm.envOr("INITIAL_FUNDING", DEFAULT_INITIAL_FUNDING);

This creates a critical mismatch between deployment assumptions and actual contract requirements.

The TreasureHunt contract relies on sufficient balance to process claims:

if (address(this).balance < REWARD) revert NotEnoughFunds();

Since each claim requires 10 ether, insufficient funding at deployment will immediately block user claims.

There is no on-chain enforcement ensuring that:

  • The contract is funded correctly

  • The funding matches MAX_TREASURES * REWARD

As a result, incorrect deployment configuration can permanently render the system unusable.


Risk

If the contract is deployed with insufficient funding (either accidentally or through misconfiguration of INITIAL_FUNDING), the system will immediately become partially or fully unusable.

Impact:

  • Users will be unable to claim rewards

  • Valid proofs will fail due to insufficient contract balance

  • The treasure hunt becomes economically non-functional

  • Requires redeployment to fix incorrect initialization

  • Breaks core protocol assumption that all treasures are claimable

Severity:

High

This is a funding-dependent denial of service at deployment time, caused by lack of enforced initialization invariants.


Proof of Concept

Step 1 — Deployment with incorrect funding

The script allows overriding funding:

uint256 initialFunding = vm.envOr("INITIAL_FUNDING", 100 ether);

An attacker or misconfigured deployer sets:

INITIAL_FUNDING=20 ether

Step 2 — Contract deployment succeeds

hunt = new TreasureHunt{value: initialFunding}(address(verifier));

Contract is deployed successfully with insufficient funds.


Step 3 — First claim execution fails

Each claim requires:

REWARD = 10 ether;

After 2 claims:

  • Balance becomes insufficient for remaining claims

  • Contract starts reverting:

revert NotEnoughFunds();

Step 4 — Result

  • Remaining users cannot claim rewards

  • Treasure hunt becomes partially unusable

  • System state is irreversibly broken without redeployment


Recommended Mitigation

Ensure deployment enforces correct funding invariants matching contract expectations.


✅ Fix 1 — Enforce correct funding in script

require(initialFunding == 100 ether, "INVALID_INITIAL_FUNDING");

✅ Fix 2 — Derive funding from contract constants

uint256 initialFunding = hunt.REWARD() * hunt.MAX_TREASURES();

✅ Fix 3 — Add runtime validation after deployment

require(
address(hunt).balance == hunt.REWARD() * hunt.MAX_TREASURES(),
"INVALID_CONTRACT_FUNDING"
);

Expected Outcome After Fix

  • Deployment always matches economic requirements

  • No risk of underfunded contract state

  • Claim system remains fully functional for all treasures

Updates

Lead Judging Commences

s3mvl4d Lead Judge 18 days ago
Submission Judgement Published
Validated
Assigned finding tags:

liquidity enforcement issues

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.

Support

FAQs

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

Give us feedback!