totalFees is a uint64 and is updated with an unchecked uint256->uint64 cast under Solidity 0.7.6 (no built-in overflow protection). Large or accumulated fees are silently truncated, so the recorded fee total no longer matches the ETH actually held for fees.
Each raffle adds its 20% fee to totalFees so that feeAddress can later withdraw the exact amount of accumulated protocol fees.
fee is a uint256 but is cast to uint64 before being added, and the addition itself is unchecked in 0.7.6. Once fee (in wei) exceeds type(uint64).max (about 18.44 ETH), or the running total exceeds it, the high bits are lost and totalFees becomes smaller than the fees truly owed.
Likelihood:
This occurs once a single raffle's fee exceeds type(uint64).max (~18.44 ETH), reachable with ~93+ entrants at a 1 ETH entrance fee.
It also occurs cumulatively, as totalFees adds up across many raffles until the uint64 ceiling is crossed.
Impact:
feeAddress can withdraw only the truncated totalFees, permanently under-collecting the real protocol fees.
The truncated totalFees no longer equals the contract balance, which also breaks the strict balance check in withdrawFees().
The following test shows that casting a fee larger than type(uint64).max to uint64 discards the high bits, so the stored value is strictly smaller than the true fee.
Store totalFees as a uint256 and remove the uint64 cast, so no truncation is possible. If the contract must stay on 0.7.6, additionally use SafeMath for the addition; upgrading to ^0.8.0 gives checked arithmetic by default.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.