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

Total rarity score used for modulo is 3 instead of 100, leading to altered allocation of Snek NFT

Summary

The winner gets a snek NFT minted on the basis of rarity of the snek via a random number derived by the chainlink VRF.

Every snek has their own chances of getting associated with them, which are mentioned by the protocol, there total sum being 100, therefore the random number was expected to be modulo by 100, in order to get the chances in the respective ranges limit but instead the actual implementation performs a modulo of 3 which is kind of irrelevant and breaks the protocol rule's to get the snek.

Vulnerability Details

  • The vulnerability occurs while allocating the random snek to the Raffle winner because of incorrect implementation to allocate the snek, as it does not follow the said percentage chances distribution of snek as mentioned by the protocol.

There are 3 NFTs that can be won in the snek raffle, each with varying rarity.
Brown Snek - 70% Chance to get
Jungle Snek - 25% Chance to get
Cosmic Snek - 5% Chance to get
  • The total % sum up to 100, therefore in order to get the randomness in the specified ranges, the random number derived via chainlink VRF should be modulo by 100.

  • But in the current implementation it performs modulo by 3 resulting into 3 numbers only -> 0, 1 or 2. As a result of which the chances of getting every snek is equally likely, which doesn't actually follow the said rules.

Impact

The protocol mentions to have custom chances of getting the respective NFT but current implementation makes getting every NFT as equally likely.

Tools Used

Manual Review

Recommendations

Implement the said custom chances distribution for the respective snek.

diff --git a/contracts/snek_raffle.vy b/contracts/snek_raffle.vy
index 34366c4..238aaea 100644
--- a/contracts/snek_raffle.vy
+++ b/contracts/snek_raffle.vy
@@ -151,8 +151,13 @@ def fulfillRandomWords(request_id: uint256, random_words: uint256[MAX_ARRAY_SIZE
self.players = []
self.raffle_state = RaffleState.OPEN
self.last_timestamp = block.timestamp
- rarity: uint256 = random_words[0] % 3
- self.tokenIdToRarity[ERC721._total_supply()] = rarity
+ rarity: uint256 = random_words[0] % 100
+ if rarity < COMMON_RARITY:
+ self.tokenIdToRarity[ERC721._total_supply()] = COMMON
+ elif rarity < COMMON_RARITY + RARE_RARITY:
+ self.tokenIdToRarity[ERC721._total_supply()] = RARE
+ else:
+ self.tokenIdToRarity[ERC721._total_supply()] = LEGEND
log WinnerPicked(recent_winner)
ERC721._mint(recent_winner, ERC721._total_supply())
send(recent_winner, self.balance)
Updates

Lead Judging Commences

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

Rarity is 1/3 instead of what the docs say

Support

FAQs

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