Generate a Random Number in Solidity Safely

Solidity can hash block data into a bounded number, but that result is not suitable for a raffle, game reward, or any outcome with value, and I compiled the small contract below with solc 0.8.36 during this refresh to show the mechanics without presenting a deterministic input as secure randomness.

Why randomness is difficult on a blockchain

A smart contract must produce the same result for every node that executes it, so hidden inputs cannot create a private random draw inside the Ethereum Virtual Machine without making nodes disagree.

Solidity exposes block.prevrandao on post-Paris Ethereum Virtual Machine chains, and the Solidity global variables reference describes it as a beacon-chain value rather than an unpredictable secret for a valuable outcome.

Generate a bounded pseudo-random number

Use this approach only for demonstrations, non-sensitive UI choices, and learning how hashing and modulo reduction fit together, where nobody benefits from predicting or influencing the result.

npm install solc
node compile.js

The compile command produced a RandomPicker contract with one pick function and 909 bytes of creation bytecode, while npm reported two dependency vulnerabilities that you should review before using the compiler in a broader toolchain.

const fs = require("fs");
const solc = require("solc");

const source = fs.readFileSync("RandomPicker.sol", "utf8");
const input = {
  language: "Solidity",
  sources: { "RandomPicker.sol": { content: source } },
  settings: { outputSelection: { "*": { "*": ["abi", "evm.bytecode.object"] } } },
};
const result = JSON.parse(solc.compile(JSON.stringify(input)));

if ((result.errors ?? []).some((issue) => issue.severity === "error")) {
  console.error(result.errors);
  process.exit(1);
}

const contract = result.contracts["RandomPicker.sol"].RandomPicker;
console.log("compiler", solc.version());
console.log("contract", "RandomPicker");
console.log("functions", contract.abi.filter((item) => item.type === "function").map((item) => item.name).join(", "));

The Solidity contract

Pass an upper bound and a nonce so the caller can request a different hash input without relying on a fixed value stored by the contract.

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

contract RandomPicker {
    function pick(uint256 upperBound, uint256 nonce) external view returns (uint256) {
        require(upperBound > 0, "upperBound must be positive");

        bytes32 digest = keccak256(
            abi.encodePacked(block.prevrandao, block.number, msg.sender, nonce)
        );
        return uint256(digest) % upperBound;
    }
}

keccak256 converts the encoded inputs into a bytes32 digest. Casting that digest to uint256 and applying modulo upperBound returns a value from 0 through upperBound minus 1.

Why the result is not secure

Every input is visible to block producers and observers, so a participant who can evaluate the inputs can calculate the output and a block producer may have incentives around a high-value result.

Adding block.timestamp, block.number, or msg.sender does not change that security boundary because hashing combines values without creating secrecy or removing an actor’s ability to inspect known inputs.

Use verifiable randomness when the outcome matters

For a lottery, NFT reveal, prize allocation, or game action with value, request randomness from a verifiable random function (VRF) service such as the request and fulfillment flow documented by Chainlink VRF v2.5.

The asynchronous flow changes your contract design, so store the request identifier, keep the request-time decision separate from fulfillment, and do not accept a late callback as permission to reroll an outcome.

Choose the source by the consequence

A deterministic hash is enough when the output has no value and serves only as a local choice, while a verifiable off-chain source is appropriate when prediction, selection, or manipulation could change who receives an asset or reward.

Before deployment, write down who can profit from knowing the output early and who can affect the inputs. If either answer names a participant, use a VRF design and inspect the provider’s network, billing, callback gas, and fulfillment guidance for your target chain.

FAQ

These boundaries answer the questions that matter before you connect a random number to an on-chain action.

Can Solidity generate a secure random number by itself?

No. Contract execution and its inputs are observable, so Solidity cannot create a private unpredictable draw for an outcome with value. Use a verifiable random function service for that case.

What does block.prevrandao do in Solidity?

On post-Paris Ethereum Virtual Machine chains, block.prevrandao exposes a beacon-chain value. It can contribute to a deterministic hash, but it does not make a high-value on-chain outcome secure.

How do I keep a Solidity random number within a range?

Convert a hash digest to uint256 and apply modulo with a positive upper bound. The result is from 0 through upperBound minus 1, but modulo reduction does not solve predictability or manipulation concerns.

Aneesha S
Aneesha S

Aneesha S writes practical guides to MongoDB, Mongoose, and Node.js. Her articles cover document queries and updates, file operations, and HTTP requests.

Articles: 169