Puppy Raffle

AI First Flight #1
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Severity: low
Valid

Rounding Errors in Prize/Fee Distribution

Root + Impact

Description

  • * The `selectWinner()` function calculates prize pool and fees using integer division: `prizePool = (totalAmountCollected * 80) / 100` and `fee = (totalAmountCollected * 20) / 100`.

    * When `totalAmountCollected` is not divisible by 5, integer division causes rounding errors. The sum of `prizePool + fee` may be less than `totalAmountCollected`, leaving leftover wei permanently stuck in the contract.

    ```solidity:131:134:src/PuppyRaffle.sol

    uint256 totalAmountCollected = players.length * entranceFee;

    uint256 prizePool = (totalAmountCollected * 80) / 100;

    uint256 fee = (totalAmountCollected * 20) / 100;

    totalFees = totalFees + uint64(fee);

    ```


Risk

Likelihood:

  • * This occurs whenever `totalAmountCollected % 5 != 0` (i.e., when the total is not divisible by 5)

    * With common entrance fees, this happens frequently (e.g., 4 players with 1 wei each = 4 wei, not divisible by 5)

    * Over many raffles, leftover wei accumulates in the contract

Impact:

  • * Small amounts of ETH accumulate and become permanently locked

    * Funds cannot be recovered or withdrawn

    * Accounting discrepancies grow over time

    * With many raffles, accumulated leftovers could become significant

Proof of Concept

```solidity
// Example: 4 players, entranceFee = 1 wei each
uint256 totalAmountCollected = 4; // 4 wei
uint256 prizePool = (4 * 80) / 100; // = 3 wei (rounds down)
uint256 fee = (4 * 20) / 100; // = 0 wei (rounds down)
// Total distributed: 3 + 0 = 3 wei
// Leftover: 1 wei stuck in contract forever
```

Recommended Mitigation

```diff
uint256 totalAmountCollected = players.length * entranceFee;
uint256 prizePool = (totalAmountCollected * 80) / 100;
- uint256 fee = (totalAmountCollected * 20) / 100;
+ uint256 fee = totalAmountCollected - prizePool; // Ensures no remainder
totalFees = totalFees + uint64(fee);
```
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 16 days ago
Submission Judgement Published
Validated
Assigned finding tags:

[L-06] Fee should be 'totalAmountCollected-prizePool' to prevent decimal loss

## Description `fee` should be 'totalAmountCollected-prizePool' to prevent decimal loss ## Vulnerability Details ``` uint256 totalAmountCollected = players.length * entranceFee; uint256 prizePool = (totalAmountCollected * 80) / 100; uint256 fee = (totalAmountCollected * 20) / 100; ``` This formula calculates `fee` should be 'totalAmountCollected-prizePool' ## Impact By calculates `fee` like the formula above can cause a loss in `totalAmountCollected' if the `prizePool` is rounded. ## Recommendations ```diff - uint256 fee = (totalAmountCollected * 20) / 100; + uint256 fee = totalAmountCollected-prizePool; ```

Support

FAQs

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

Give us feedback!