I am currently re-learning Solidity to sharpen my skills and writing a "WTF Solidity Quick Start" guide for beginners to use (advanced programmers can look for other tutorials). I will update 1-3 lectures every week.
Twitter: @0xAA_Science
Discord: WTF Academy
All code and tutorials are open source on GitHub: github.com/AmazingAng/WTF-Solidity
Many Ethereum applications require the use of random numbers, such as NFT random tokenId selection, blind box drawing, and randomly determining the winner in gamefi battles. However, since all data on Ethereum is public and deterministic, it cannot provide developers with a method of generating random numbers like other programming languages. In this tutorial, we will introduce two methods of on-chain (hash function) and off-chain (Chainlink oracle) random number generation, and use them to create a tokenId random minting NFT.
On-chain Random Number Generation
We can use some on-chain global variables as seeds and use the keccak256()
hash function to obtain pseudo-random numbers. This is because the hash function has sensitivity and uniformity, and can generate "apparently" random results. The getRandomOnchain()
function below uses the global variables block.timestamp
, msg.sender
, and blockhash(block.number-1)
as seeds to obtain random numbers:
Note: This method is not secure:
- Firstly, variables such as
block.timestamp
,msg.sender
, andblockhash(block.number-1)
are all publicly visible. Users can predict the random number generated by these seeds and select the output they want to execute the smart contract. - Secondly, miners can manipulate
blockhash
andblock.timestamp
to generate a random number that suits their interests.
However, this method is the most convenient on-chain random number generation method, and many project parties rely on it to generate insecure random numbers, including well-known projects such as meebits
and loots
. Of course, all these projects have been attacked: attackers can forge any rare NFT
they want, instead of randomly drawing them.
Off-chain random number generation
We can generate random numbers off-chain and upload them to the chain through oracles. Chainlink provides a VRF (Verifiable Random Function) service, and on-chain developers can pay the LINK token to obtain a random number. Chainlink VRF has two versions. Since the second version requires registration on the official website and prepaid fees, and the usage is similar, only the first version VRF v1 is introduced here.
Steps to use Chainlink VRF

We will use a simple contract to introduce the steps to use Chainlink VRF. The RandomNumberConsumer
contract can request a random number from the VRF and store it in the state variable randomResult
.
1. The user contract inherits from VRFConsumerBase
and transfers the LINK
token
To use VRF to obtain a random number, the contract needs to inherit the VRFConsumerBase
contract and initialize the VRF Coordinator
address, LINK
token address, unique identifier Key Hash
, and usage fee fee
in the constructor.
Note: Different chains correspond to different parameters, please refer to here to find out.
In the tutorial, we use the Rinkeby
testnet. After deploying the contract, users need to transfer some LINK
tokens to the contract. Testnet LINK
tokens can be obtained from the LINK faucet.
2. User requests random number through contract
Users can call requestRandomness()
inherited from the VRFConsumerBase
contract to request a random number and receive a request identifier requestId
. This request will be passed on to the VRF
contract.
-
The
Chainlink
node generates a random number and a digital signature off-chain and sends them to theVRF
contract. -
The
VRF
contract verifies the validity of the signature. -
The user contract receives and uses the random number.
After verifying the validity of the signature in the VRF
contract, the fallback function fulfillRandomness()
of the user contract will be automatically called, and the off-chain generated random number will be sent over. The logic of consuming the random number should be implemented in this function.
Note: The requestRandomness()
function is called by the user to request a random number and the fallback function fulfillRandomness()
is called when the VRF
contract returns the random number are two separate transactions, with the user contract and the VRF
contract being the callers, respectively. The latter will be a few minutes later than the former (with different chain delays).
tokenId
Randomly Minted NFT
In this section, we will use on-chain and off-chain random numbers to create a tokenId
randomly minted NFT
. The Random
contract inherits from both the ERC721
and VRFConsumerBase
contracts.
State Variables
NFT
relatedtotalSupply
: Total supply ofNFT
.ids
: Array used for calculatingtokenId
s that can beminted
, seepickRandomUniqueId()
function.mintCount
: Number of NFTs that have beenminted
.
Chainlink VRF
relatedkeyHash
: Unique identifier forVRF
.fee
:VRF
fee.requestToSender
: Records the user address that applied forVRF
for minting.
Constructor
Initialize the relevant variables of the inherited VRFConsumerBase
and ERC721
contracts.
Other Functions
In addition to the constructor function, the contract defines 5 other functions:
-
pickRandomUniqueId()
: takes in a random number and returns atokenId
that can be used for minting. -
getRandomOnchain()
: returns an on-chain random number (insecure). -
mintRandomOnchain()
: mints an NFT using an on-chain random number, and callsgetRandomOnchain()
andpickRandomUniqueId()
. -
mintRandomVRF()
: requests a random number fromChainlink VRF
to mint an NFT. Since the logic for minting with a random number is in the callback functionfulfillRandomness()
, which is called by theVRF
contract, not the user minting the NFT, the function here must use therequestToSender
state variable to record the user address corresponding to theVRF
request identifier. -
fulfillRandomness()
: the callback function forVRF
, which is automatically called by theVRF
contract after verifying the authenticity of the random number. It uses the returned off-chain random number to mint an NFT.
remix
Verification
1. Deploy the Random
contract on the Rinkeby
testnet

2. Get LINK
and ETH
on the Rinkeby
testnet using Chainlink
faucet

3. Transfer LINK
tokens into the Random
contract
After the contract is deployed, copy the contract address, and transfer LINK
to the contract address just as you would for a normal transfer.

4. Mint NFTs using on-chain random numbers
In the remix
interface, click on the orange function mintRandomOnchain
on the left side

Metamask
to start minting the transaction using on-chain random numbers.

5. Mint NFTs using Chainlink VRF
off-chain random numbers
Similarly, in the remix
interface, click on the orange function mintRandomVRF
on the left and click confirm in the pop-up little fox wallet. The transaction of minting an NFT
using Chainlink VRF
off-chain random number has started.
Note: when using VRF
to mint NFT
, initiating the transaction and the success of minting is not in the same block.


6. Verify that the NFT
has been minted
From the above screenshots, it can be seen that in this example, the NFT
with tokenId=87
has been randomly minted on-chain, and the NFT
with tokenId=77
has been minted using VRF
.
Conclusion
Generating a random number in Solidity
is not as straightforward as in other programming languages. In this tutorial, we introduced two methods of generating random numbers on-chain (using hash functions) and off-chain (Chainlink
oracle), and used them to create an NFT
with a randomly assigned tokenId
. Both methods have their advantages and disadvantages: using on-chain random numbers is efficient but insecure while generating off-chain random numbers relies on third-party Oracle services, which is relatively safe but not as easy and economical. Project teams should choose the appropriate method according to their specific business needs.
Apart from these methods, there are other organizations that are trying new ways of RNG (Random Number Generation), such as randao, which proposes to provide an on-chain and true randomness service in a DAO pattern.