Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Smart Contracts Without `fallback()` or `receive()` Functions Cannot Win a Snek-Raffle

Summary

If a smart contract enters the raffle and it does not implement the receive() or fallback() function, the call used to send the funds to the winner will fail to execute, compromising the functionality of the protocol.

It is also important to note that the official VRF Security Considerations by Chainlink indicate that the call to fulfillRandomWords() must not revert.

Vulnerability Details

The vulnerability comes from the way smart contracts work in EVM chains; if a smart contract does not implement the receive() and/or fallback() function, it will not accept incoming ETH. Therefore, the highlighted line of the code snippet below would revert if the recent_winner is a contract that does not agree to receive ether.

@internal
def fulfillRandomWords(request_id: uint256, random_words: uint256[MAX_ARRAY_SIZE]):
index_of_winner: uint256 = random_words[0] % len(self.players)
recent_winner: address = self.players[index_of_winner]
self.recent_winner = recent_winner
self.players = []
self.raffle_state = RaffleState.OPEN
self.last_timestamp = block.timestamp
rarity: uint256 = random_words[0] % 3
self.tokenIdToRarity[ERC721._total_supply()] = rarity
log WinnerPicked(recent_winner)
ERC721._mint(recent_winner, ERC721._total_supply())
@> send(recent_winner, self.balance)

Impact

The protocol will not be able to select a winner and send funds to it if that winner is a smart contract that does not accept ETH.

Tools Used

Manual analysis.

Recommendations

Consider updating the implementation of the fulfillRandomWords() function to make sure it never reverts, as recommended by the official VRF Security Considerations. For this, Chainlink suggests the following generic recommendation: "Consider simply storing the randomness and taking more complex follow-on actions in separate contract calls made by you, your users, or an Automation Node".

Furthermore, consider changing the highlighted send() above for a raw_call() that handles the case where recipients do not accept ETH. Finally, for such cases, the contract could take note of the winner and let it call a new function to indicate where to send the prize.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Winner can be a contract that refuses ETH and brinks the whole contract + reverts on Chainlink VRF

Support

FAQs

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