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

Incorrect rarity calculation leads to 1 in 3 chance to get each snek type

Summary

When a user wins a raffle, they are to be minted a new snek. What type of snek they win is to be determined as a percentage chance based on that types rarity, here is an excerpt from the docs that confirms this:

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

However, currently the rarity is just being determined by a random 1/3 chance using the Chainlink VRF random value.

Vulnerability Details

The rarity calculation takes place in fullfillRandomWords() as part of the callback for Chainlink VRF. The current rarity calculation is as follows:

rarity: uint256 = random_words[0] % 3

random_words[0] is the only element of an array of random uint256's generated by Chainlink VRF, by taking the modulus of 3 of that element, you 1 of 3 random numbers, 0, if the andom_words[0] divides evenly by 3, 1 if there would be a remained of one, or two if there would be a remained of 2. This results in a rarity chance that is 1/3 for each rarity type. As we can see from the rarity constants (and earlier in the docs):

COMMON_RARITY: public(constant(uint256)) = 70
RARE_RARITY: public(constant(uint256)) = 25
LEGEND_RARITY: public(constant(uint256)) = 5

The intended rarity is 70% for common, 25% for rare and 5% for legendary.

Impact

This results in there being no rarity diversity between the snek nfts, seriously hurting their potential value as there is no supply difference between a common snek and a legendary snek.

Tools Used

Manual Review

Recommendations

I recommend you remove the current rarity logic and replace it with the following, or equivalent.

- rarity: uint256 = random_words[0] % 3
+ rarity_value: uint256 = random_words[0] % 100 # Map the randomness to a 0-99 range
if rarity_value < COMMON_RARITY:
rarity: uint256 = COMMON
elif rarity_value < COMMON_RARITY + RARE_RARITY:
rarity: uint256 = RARE
else:
rarity: uint256 = LEGEND
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.