Puppy Raffle

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

Smart contract wallet raffle winners without a `receive` or `fallback` function will block the start of a new lottery.

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

  • Explain the specific issue or problem in one or more sentences

**Description:** The `PuppyRaffle::selectWinner` is responsible for reseting the lottery. if the winner is a smart contract wallet that rejects payment, the lottery will not be able to restart.
Users could easily call the `selectWinner` function again and non-wallet entrants could enter, but it could cost a lot due to the duplicate check and a lottery reset could get challenging.

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

  • Reason 2

Impact:

  • Impact 1

  • Impact 2

Proof of Concept

**Proof of Concept:**
1. 10 smart contract wallet enter the lottery without a `fallback` or `receive` function.
2. The lottery ends
3. The `selectWinner` function won't work even though the lottery is over!
<details>
<summary>Code</summary>
```solidity
function testRevertOnSelectWinnerAttack() public {
WinnerOfTheRaffle winnerOfTheRaffle = new WinnerOfTheRaffle();
address[] memory players = new address[](4);
players[0] = playerOne;
players[1] = playerTwo;
players[2] = address(winnerOfTheRaffle); // Attacker
players[3] = playerFour;
puppyRaffle.enterRaffle{value: entranceFee * 4}(players);
uint256 winnerIndex = uint256(keccak256(abi.encodePacked(players[2], block.timestamp, block.difficulty))) % players.length;
if(winnerIndex == 2) {
vm.expectRevert();
puppyRaffle.selectWinner();
}
}
```
</details>

Recommended Mitigation

**Recommended Mitigation:**
1. Do not allow smart contract wallets entrants (not recommended)
2. Create a mapping of addresses -> payout amounts so winners can pull there funds out themselves with a new `claimPrize` function, putting the owness on the winner to claim their prize (Recommended).
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Validated
Assigned finding tags:

[M-03] Impossible to win raffle if the winner is a smart contract without a fallback function

## Description If a player submits a smart contract as a player, and if it doesn't implement the `receive()` or `fallback()` function, the call use to send the funds to the winner will fail to execute, compromising the functionality of the protocol. ## Vulnerability Details The vulnerability comes from the way that are programmed smart contracts, if the smart contract doesn't implement a `receive() payable` or `fallback() payable` functions, it is not possible to send ether to the program. ## Impact High - Medium: The protocol won't be able to select a winner but players will be able to withdraw funds with the `refund()` function ## Recommendations Restrict access to the raffle to only EOAs (Externally Owned Accounts), by checking if the passed address in enterRaffle is a smart contract, if it is we revert the transaction. We can easily implement this check into the function because of the Adress library from OppenZeppelin. I'll add this replace `enterRaffle()` with these lines of code: ```solidity function enterRaffle(address[] memory newPlayers) public payable { require(msg.value == entranceFee * newPlayers.length, "PuppyRaffle: Must send enough to enter raffle"); for (uint256 i = 0; i < newPlayers.length; i++) { require(Address.isContract(newPlayers[i]) == false, "The players need to be EOAs"); players.push(newPlayers[i]); } // Check for duplicates for (uint256 i = 0; i < players.length - 1; i++) { for (uint256 j = i + 1; j < players.length; j++) { require(players[i] != players[j], "PuppyRaffle: Duplicate player"); } } emit RaffleEnter(newPlayers); } ```

Support

FAQs

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

Give us feedback!