Root Cause: totalFees is declared as uint64 (max ~18.4 ETH) in Solidity 0.7.6 which lacks overflow protection, causing silent wraparound when fees exceed this limit.
Impact: Protocol permanently loses track of accumulated fees, owner cannot withdraw correct amounts, and significant revenue is lost to overflow.
Normal Behavior: The totalFees variable should accurately track all fees collected from raffles so the owner can withdraw them later.
Issue: Solidity 0.7.6 does not have built-in overflow protection, and totalFees is declared as uint64 which has a maximum value of ~18.4 ETH. When fees exceed this value, the variable overflows and wraps around to a small number, causing permanent loss of fees.
Likelihood:HIGH
Reason 1 : With 1 ETH entrance fee and 100 players per raffle, overflow happens after ~92 raffles
Reason 2: Occurs whenever cumulative fees exceed ~18.4 ETH
Impact:
Impact 1:Owner cannot withdraw legitimate fees (they become locked or understated)
Impact 2: Protocol permanently loses track of collected fees
In Solidity versions before 0.8.0, arithmetic operations don't check for overflow/underflow. When a uint64 reaches its maximum value and you add more, it "wraps around" to start from 0 again.
Use uint256: This type can hold up to ~1.15 × 10^77, which is practically unlimited for tracking ETH
Upgrade Solidity: Version 0.8.0+ has built-in overflow checks that revert on overflow
## Description ## Vulnerability Details The type conversion from uint256 to uint64 in the expression 'totalFees = totalFees + uint64(fee)' may potentially cause overflow problems if the 'fee' exceeds the maximum value that a uint64 can accommodate (2^64 - 1). ```javascript totalFees = totalFees + uint64(fee); ``` ## POC <details> <summary>Code</summary> ```javascript function testOverflow() public { uint256 initialBalance = address(puppyRaffle).balance; // This value is greater than the maximum value a uint64 can hold uint256 fee = 2**64; // Send ether to the contract (bool success, ) = address(puppyRaffle).call{value: fee}(""); assertTrue(success); uint256 finalBalance = address(puppyRaffle).balance; // Check if the contract's balance increased by the expected amount assertEq(finalBalance, initialBalance + fee); } ``` </details> In this test, assertTrue(success) checks if the ether was successfully sent to the contract, and assertEq(finalBalance, initialBalance + fee) checks if the contract's balance increased by the expected amount. If the balance didn't increase as expected, it could indicate an overflow. ## Impact This could consequently lead to inaccuracies in the computation of 'totalFees'. ## Recommendations To resolve this issue, you should change the data type of `totalFees` from `uint64` to `uint256`. This will prevent any potential overflow issues, as `uint256` can accommodate much larger numbers than `uint64`. Here's how you can do it: Change the declaration of `totalFees` from: ```javascript uint64 public totalFees = 0; ``` to: ```jasvascript uint256 public totalFees = 0; ``` And update the line where `totalFees` is updated from: ```diff - totalFees = totalFees + uint64(fee); + totalFees = totalFees + fee; ``` This way, you ensure that the data types are consistent and can handle the range of values that your contract may encounter.
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.