Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: medium
Invalid

Users can enter the raffle multiple times

Summary

Users can call the enter_raffle function multiple times and take advantage in the raffle.

Vulnerability Details

The function enter_raffle allows the users to participate in raffle. The function checks the amount of msg.value and the status of the raffle. If the msg.value is equal to ENTRANCE_FEE and the raffle is open, the function adds the caller to the players array.

def enter_raffle():
"""Enter the raffle by sending the entrance fee."""
assert msg.value == ENTRANCE_FEE, ERROR_SEND_MORE_TO_ENTER_RAFFLE
assert self.raffle_state == RaffleState.OPEN, ERROR_RAFFLE_NOT_OPEN
@> self.players.append(msg.sender)
log RaffleEntered(msg.sender)

But the function doesn't check if the caller is not already participant in the raffle. The caller can multiple times enter the raffle and increases the chance to win the raffle.

Impact

The test test_enter_raffle_multiple_times shows that the USER can enter raffle multiple times (in this test case - twice).
You can add the test to the snek_raffle_test.py and execute it using the command pytest -k 'test_enter_raffle_multiple_times' -s .

def test_enter_raffle_multiple_times(raffle_boa, entrance_fee):
boa.env.set_balance(USER, STARTING_BALANCE * 2)
with boa.env.prank(USER):
raffle_boa.enter_raffle(value=entrance_fee)
assert raffle_boa.get_players(0) == USER
with boa.env.prank(USER):
raffle_boa.enter_raffle(value=entrance_fee)
assert raffle_boa.get_players(1) == USER

In that way the USER increases the chance to win the raffle

Tools Used

VS Code, pytest

Recommendations

Add a check to the enter_raffle function to ensure that the caller is not already participant in the raffle:

def enter_raffle():
"""Enter the raffle by sending the entrance fee."""
# @audit-reported if the caller sends more than ENTRANCE_FEE, the caller will receive incorrect error message.
assert msg.value == ENTRANCE_FEE, ERROR_SEND_MORE_TO_ENTER_RAFFLE
assert self.raffle_state == RaffleState.OPEN, ERROR_RAFFLE_NOT_OPEN
+ for player: address in self.players:
+ assert msg.sender != player, ERROR_ALREADY_PARTICIPANT
self.players.append(msg.sender)
log RaffleEntered(msg.sender)

Add also the error: ERROR_ALREADY_PARTICIPANT: constant(String[46]) = "SnekRaffle: You are already a participant"

The above suggestion uses iteration over players array. Maybe more gas efficient way is to create a mapping that stores if a given address is a participant addresses: public(HashMap[address, bool]) and to check directly in the mapping:

def enter_raffle():
"""Enter the raffle by sending the entrance fee."""
assert msg.value == ENTRANCE_FEE, ERROR_SEND_MORE_TO_ENTER_RAFFLE
assert self.raffle_state == RaffleState.OPEN, ERROR_RAFFLE_NOT_OPEN
+ assert not self.addresses[msg.sender], ERROR_ALREADY_PARTICIPANT
+ self.addresses[msg.sender] = True
self.players.append(msg.sender)
log RaffleEntered(msg.sender)
Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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