True randomness on a deterministic blockchain is impossible by definition. Every node must reach the same result, which means every input must be predictable. Yet many smart contracts need randomness — for games, NFT minting, lottery selection, and random assignment. Using block.timestamp, blockhash, or any other on-chain value as randomness is exploitable. Here's why and what to do instead.
Why On-Chain Randomness Fails
// ALL OF THESE ARE EXPLOITABLE:
uint256 random1 = uint256(blockhash(block.number - 1)); // Known to validators
uint256 random2 = uint256(keccak256(abi.encodePacked(block.timestamp))); // Manipulable
uint256 random3 = uint256(keccak256(abi.encodePacked(block.difficulty))); // Predictable post-merge
uint256 random4 = uint256(keccak256(abi.encodePacked(
block.timestamp, block.number, msg.sender // All known/manipulable
)));
Block proposers know all block-level variables before proposing. They can: choose not to propose unfavorable blocks (block withholding), manipulate timestamp within tolerance, or compute the "random" value in advance and act accordingly.
Solutions for Secure Randomness
1. Chainlink VRF (Recommended)
Chainlink VRF (Verifiable Random Function) generates provably fair random numbers off-chain, with on-chain proofs that the result wasn't manipulated.
import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2Plus.sol";
contract SecureLottery is VRFConsumerBaseV2Plus {
function requestRandomWinner() external onlyOwner {
requestRandomWords(VRFV2PlusClient.RandomWordsRequest({
keyHash: keyHash,
subId: subscriptionId,
requestConfirmations: 3,
callbackGasLimit: 200000,
numWords: 1,
extraArgs: ""
}));
}
function fulfillRandomWords(uint256, uint256[] calldata randomWords) internal override {
uint256 winnerIndex = randomWords[0] % participants.length;
winner = participants[winnerIndex];
}
}
2. Commit-Reveal with Multiple Parties
Multiple participants commit secret values, then reveal them. The final random number is the XOR or hash of all reveals. Secure as long as at least one participant is honest.
3. RANDAO (Post-Merge Ethereum)
Ethereum's RANDAO beacon (block.prevrandao) provides better randomness than block hash but is still manipulable by block proposers. Acceptable for low-stakes applications.
When to Use What
| Use Case | Recommended Solution |
|---|---|
| Lottery / high-value games | Chainlink VRF |
| NFT trait randomness | Chainlink VRF |
| Random assignment (low stakes) | RANDAO / prevrandao |
| On-chain games (turn-based) | Commit-reveal |
- ✅ Use Chainlink VRF for any randomness involving value
- ✅ Never use block.timestamp, blockhash, or block.number as randomness
- ✅ Handle the async nature of VRF (request → callback pattern)
- ✅ Set appropriate confirmation blocks for VRF requests
Predictable randomness is the same as no randomness. Audit your RNG implementation before someone predicts your next winning number.