Puppy Raffle

AI First Flight #1
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

Low 1 Total Fees and Select The Winner

Root + Impact

Description

totalFees is declared as uint64 (line 30), and selectWinner() casts the uint256 fee down to uint64 before accumulating it (line 134):

uint256 fee = (totalAmountCollected * 20) / 100;
@> totalFees = totalFees + uint64(fee);

In Solidity 0.7.6 there is no built-in overflow check on the cast — a value exceeding uint64 capacity is silently truncated (wrapping), so the recorded fee no longer matches the fee actually owed. Corrupted totalFees then breaks withdrawFees(), whose address(this).balance == totalFees check (line 158) can never hold against a truncated book value.

Stated honestly on reachability: triggering the truncation requires fee > type(uint64).max (~18.4 ETH), i.e. an entranceFee above ~90 ETH per player — far outside any realistic deployment of this raffle. This is therefore a code-hygiene / defense-in-depth finding, not a practical exploit path: the cast is a latent accounting hazard that becomes real the moment deployment parameters grow.

Risk

Likelihood: Low — exploitation needs an unrealistically large entranceFee (above ~90 ETH per player).

Impact: Low — in the unrealistic regime, fee accounting is silently corrupted and withdrawFees() can brick; in realistic deployments there is no impact.

Proof of Concept

Not demonstrated with a runnable PoC, deliberately: reaching the truncation requires an entranceFee above ~90 ETH per player, which is outside any realistic deployment. The arithmetic is straightforward to verify by inspection — uint64 wraps at 2^64 - 1 wei (~18.4 ETH), and Solidity 0.7.6 performs the uint256 → uint64 conversion with silent truncation:

// type(uint64).max = 18446744073709551615 wei (~18.4 ETH)
// fee = (players.length * entranceFee * 20) / 100
// truncation iff fee > 18446744073709551615, i.e. entranceFee > ~92 ETH per player

Recommended Mitigation

Declare totalFees as uint256 so no narrowing cast exists at all. With the full 256-bit domain, the accumulation is exact for any deployment parameter — the truncation hazard disappears by construction rather than by hoping parameters stay small.

- uint64 public totalFees = 0;
+ uint256 public totalFees = 0;
- totalFees = totalFees + uint64(fee);
+ totalFees = totalFees + fee;
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 1 hour ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!