You cannot create truly random numbers but you can pseudo-random numbers. Blockchain is a deterministic system so we have to make sure that each node must give the same random number.
Determinism, is very important because it is vital that
regardless of where the smart contract code executes, it produces the same result every time and
everywhere.
Getting truly numbers in a deterministic system is impossible because global variables are being used is predictable or can be somehow get manipulated.
For example, Timestamp vulnerability is quite common. Usually, the timestamp
of a block is accessed via block.timestamp
but this timestamp can be manipulated by miners, leading to influencing the outcome of some function that relies on timestamps. The timestamp is used as a source of randomness in lottery games to select the next winner. Thus, it might be possible for a miner to modify the timestamp in such a way that its chances of becoming the next winner increase.
In order to get the true random number we have to look at outside the blockchain. We need to use oracle services to get the true random number. If you do not have your true random number in your smart contract, your smart contract can get hacked. You can read about 2 cases:
https://www.reddit.com/r/ethereum/comments/74d3dc/smartbillions_lottery_contract_just_got_hacked/
https://hrishiolickel.medium.com/why-smart-contracts-fail-undiscovered-bugs-and-what-we-can-do-about-them-119aa2843007
since solidity evolving very fast, other answers are outdated. This answer will be outdated one day but as of now you can implement a pseudo number generator like this:
// I realized if you call the random() in for loop, you get same result. So I had to add another changing variable
// https://mcmap.net/q/503711/-how-to-generate-random-words-in-solidity-based-on-a-string-of-letters/73557284#73557284
uint counter =1;
function random() private view returns (uint) {
counter++;
// sha3 and now have been deprecated
return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, players,counter)));
// convert hash to integer
// players is an array of entrants
}
this will return very very big number. But we use modulo operator.
random() % players.length
this will return a number between 0 and players.length. we write a function for this:
function pickWinner() public {
uint index=random()%players.length;
}
Get random Number with Chainlink
After you deploy this contract, you have to send Link tokens to this contract and press on getRandomNumber
. Wait about a minute and then click on result
import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/VRFConsumerBase.sol";
contract Test is VRFConsumerBase {
bytes32 public keyHash;
uint256 public fee;
uint256 public ticketPrice;
uint256 public result;
// ---------------- GOERLI ADDRESSESS----------------
// link address 0x326C977E6efc84E512bB9C30f76E30c160eD06FB
// key hash 0x0476f9a745b61ea5c0ab224d3a6e4c99f0b02fce4da01143a4f70aa80ae76e8a
//ChainlinkVRFCoordinator 0x2bce784e69d2Ff36c71edcB9F88358dB0DfB55b4
constructor(
address _ChainlinkVRFCoordinator,
address _ChainlinkLINKToken,
bytes32 _ChainlinkKeyHash,
uint256 _ticketPrice
) VRFConsumerBase(_ChainlinkVRFCoordinator, _ChainlinkLINKToken) {
keyHash = _ChainlinkKeyHash;
fee = 0.1 * 10 ** 18;
ticketPrice = _ticketPrice;
}
function getRandomNumber() public payable returns (bytes32 requestId) {
require(
LINK.balanceOf(address(this)) >= fee,
"YOU HAVE TO SEND LINK TOKEN TO THIS CONTRACT"
);
return requestRandomness(keyHash, fee);
}
// this is callback, it will be called by the vrf coordinator
function fulfillRandomness(
bytes32 requestId,
uint256 randomness
) internal override {
result = randomness;
}
receive() external payable {}
}
I answered this question: Getting a random number with Chainlink VRF