I'm building a lottery game contract and I need to get a range of values from chainlink VRF's number. The range is the length of the players list. For simplicity, I've made a simple contract that demonstrates my idea of how to get a ranged random number from Chainlink's VRF number. To do this I have decided to take the modulo N of Chainlink's number, where N is the length of the players list.
My worry is that this process makes the number less random, more biased and therefore not fair. Am I right to worry? If so, what's a better approach for this problem?
pragma solidity ^0.8.0;
contract GetRandomNumber{
string[] playersList = ["bob", "alice", "lux", "ahri", "nunu", "amumu", "jax", "olaf", "jinx", "vayne", "twitch", "alistar", "annie", "leona", "warwick"];
uint256 public randomNumber;
//actual random number from Chainlink VRF
uint256[] chainlinkVRFNumberList = [64062631830175213092191689838209884690462398265195175129745934432936884152163];
uint256 public chainlinkVRFNumber = chainlinkVRFNumberList[0];
constructor(){
getNumber(chainlinkVRFNumber);
}
function getNumber(uint256 x) internal {
uint256 value = (x % playersList.length) + 1;
randomNumber = value;
}
}```
Best Answer
Since the number returned by Chainlink VRF is verifiably random, that's the important source of randomness. The N is not really important for randomness as the N may or may not hold a constant value over the lifetime of calls.
but since the VRF-returned uint is random, the result of applying modulo N to that number will be random too.
as a simple thought experiment, randomly pick numbers in your head between 0 and 20 and apply modulo 4 (or another number). The results will be different and "random" based on how randomly you picked your numbers.
This is assuming you discard the VRF-returned number each time and use a new one. Re-using a random number for several calculations may drift towards less randomness.
Your approach is fine and is consistent with best practices for getting a random number within a range.