SNARKeling Treasure Hunt

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

Constructor Lacks Minimum Funding Check

Root + Impact

Description

  • The constructor accepts any amount of ETH via the payable keyword without validating that the contract receives sufficient funds to cover the expected treasure rewards. The protocol documentation states the contract should be funded with 100 ETH (10 treasures x 10 ETH), but there is no on-chain enforcement of this minimum.

// @>TreasureHunt.constructor()
constructor(address _verifier) payable {
if (_verifier == address(0)) revert InvalidVerifier();
owner = msg.sender;
verifier = IVerifier(_verifier);
paused = false;
// Owner should fund 100 ETH at deployment (10 treasures × 10 ETH).
} //Lacks Minimum Funding Check

Risk

Likelihood:

  • Reason 1: the owner initiates the TreasureHunt contract with less than 100 ETH


Impact:

  • Impact: Contract can be deployed with insufficient funds (e.g., 1 ETH instead of 100 ETH)

  • Early claimants may successfully claim treasures but later claimants find the contract has insufficient balance

  • Creates a "race to claim" dynamic where late participants are denied their expected rewards

  • Damages protocol credibility and trust


Proof of Concept

A owner funds the TreasureHunt contract with 85 Eth.

8 hunters successfully claimed the rewards.

There is no other hunters can successfully claim rewards due to lack of funds.

Recommended Mitigation

Add a minimum funding check in the contract constructor:

constructor(address _verifier) payable {
if (_verifier == address(0)) revert InvalidVerifier();
+ require (msg.value >= REWARD * MAX_TREASURES, "InsufficientFunding");
owner = msg.sender;
verifier = IVerifier(_verifier);
paused = false;
}
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!