Requirements
This guide assumes that you have basic knowledge about writing and deploying smart contracts. If you are new to smart contract development, read the Consuming Data Feeds guide before you begin.
Note
The video uses a seed phrase to request randomness. This feature is deprecated. Refer to the code in this tutorial for the most up to date procedures.
In this guide, you will learn about generating randomness on blockchains. This includes learning how to implement a Request and Receive cycle with Chainlink oracles and how to consume random numbers with Chainlink VRF in smart contracts.
Table of Contents
rollDice?Randomness is very difficult to generate on blockchains. This is because every node on the blockchain must come to the same conclusion and form a consensus. Even though random numbers are versatile and useful in a variety of blockchain applications, they cannot be generated natively in smart contracts. The solution to this issue is Chainlink VRF, also known as Chainlink Verifiable Random Function.
The previous guide explained how to consume Chainlink Data Feeds, which consist of reference data posted on-chain by oracles. This data is stored in a contract and can be referenced by consumers until the oracle updates the data again.
Randomness, on the other hand, cannot be reference data. If the result of randomness is stored on-chain, any actor could retrieve the value and predict the outcome. Instead, randomness must be requested from an oracle, which generates a number and a cryptographic proof. Then, the oracle returns that result to the contract that requested it. This sequence is known as the Request and Receive cycle.
In return for providing the service of generating a random number, oracles are paid in LINK. The contract that requests the randomness must pay for the service during the request.
LINK conforms to the ERC-677 token standard, which is an extension of ERC-20. This standard is what enables data to be encoded in token transfers. This is integral to the Request and Receive cycle.
Smart contracts have all the capabilities that wallets have in that they are able to own and interact with tokens. The contract that requests randomness from Chainlink VRF must have a LINK balance greater than or equal to the cost to make the request in order to pay and fulfill the service.
For example, if the current price of Chainlink VRF is 0.1 LINK, the requesting contract must hold at least 0.1 LINK to pay for the request. Once the request transaction is complete, the oracle generates the random number and sends the result back.
To see a basic implementation of Chainlink VRF, see Get a Random Number. In this section, you will create an application that uses Chainlink VRF to generate randomness. The contract used in this application will have a Game of Thrones theme.
The contract will request randomness from Chainlink VRF. The result of the randomness will transform into a number between 1 and 20, mimicking the rolling of a 20 sided die. Each number represents a Game of Thrones house. If the dice land on the value 1, the user is assigned house Targaryan, 2 for Lannister, and so on. A full list of houses can be found here.
When rolling the die, it will accept an address variable to track which address is assigned to each house.
The contract will have the following functions:
rollDice: This submits a randomness request to Chainlink VRFfulfillRandomness: The function that the Oracle uses to send the result backhouse: To see the assigned house of an addressNote: to jump straight to the entire implementation, you can open the VRFD20.sol contract in remix.
VRFConsumerBaseChainlink maintains a library of contracts that make consuming data from oracles easier. For Chainlink VRF, you will use a contract named VRFConsumerBase that must be imported and extended from the contract that you create.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
contract VRFD20 is VRFConsumerBase {
}
The contract will contain multiple objects. Each oracle job has a unique key hash that identifies the tasks that it should perform. The contract will store the Key Hash that identifies Chainlink VRF and the fee amount to use in the request.
bytes32 private s_keyHash;
uint256 private s_fee;
To keep track of addresses that roll the dice, the contract uses mappings. Mappings are unique key-value pair data structures similar to hash tables in Java.
mapping(bytes32 => address) private s_rollers;
mapping(address => uint256) private s_results;
s_rollers stores a mapping between the requestID (returned when a request is made), and the address of the roller. This is so the contract can keep track of who to assign the result to when it comes back.s_results stores the roller and the result of the dice roll.The fee and the key hash must be initialized in the constructor of the contract. To use VRFConsumerBase properly, you must also pass certain values into its constructor. Below are the values for Kovan testnet network. They will be updated in the constructor.
The address that creates the smart contract is the owner of the contract. Only the owner is allowed to do some tasks. Import a contract named ConfirmedOwner and use it to extend the contract that you create.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
contract VRFD20 is VRFConsumerBase, ConfirmedOwner(msg.sender) {
// variables
bytes32 private s_keyHash;
uint256 private s_fee;
mapping(bytes32 => address) private s_rollers;
mapping(address => uint256) private s_results;
// constructor
constructor(address vrfCoordinator, address link, bytes32 keyHash, uint256 fee)
VRFConsumerBase(vrfCoordinator, link)
{
s_keyHash = keyHash;
s_fee = fee;
}
}
As you can see, the VRFConsumerBase constructor requires both the VRF Coordinator address and the LINK token address. For the Kovan test network, use the following values:
You can find the addresses for other networks in the VRF contracts page.
rollDice functionThe rollDice function will complete the following tasks:
requestId and roller address.You must add a ROLL_IN_PROGRESS constant to signify that the die has been rolled but the result is not yet returned. Also add a DiceRolled event to the contract.
Only the owner of the contract can execute the rollDice function.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
contract VRFD20 is VRFConsumerBase, ConfirmedOwner(msg.sender) {
// variables
uint256 private constant ROLL_IN_PROGRESS = 42;
// ...
// { variables that are already written }
// ...
// events
event DiceRolled(bytes32 indexed requestId, address indexed roller);
// ...
// { constructor }
// ...
// rollDice function
function rollDice(address roller) public onlyOwner returns (bytes32 requestId) {
// checking LINK balance
require(LINK.balanceOf(address(this)) >= s_fee, "Not enough LINK to pay fee");
// checking if roller has already rolled die
require(s_results[roller] == 0, "Already rolled");
// requesting randomness
requestId = requestRandomness(s_keyHash, s_fee);
// storing requestId and roller address
s_rollers[requestId] = roller;
// emitting event to signal rolling of die
s_results[roller] = ROLL_IN_PROGRESS;
emit DiceRolled(requestId, roller);
}
}
fulfillRandomness functionfulfillRandomness is a special function defined within the VRFConsumerBase contract that our contract extends from. The coordinator sends the result of our generated randomness back to fulfillRandomness. You will implement some functionality here to deal with the result:
s_results mapping variable.DiceLanded event.// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
contract VRFD20 is VRFConsumerBase, ConfirmedOwner(msg.sender) {
// ...
// { variables }
// ...
// events
// ...
// { events that are already written }
// ...
event DiceLanded(bytes32 indexed requestId, uint256 indexed result);
// ...
// { constructor }
// ...
// ...
// { rollDice function }
// ...
// fulfillRandomness function
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
// transform the result to a number between 1 and 20 inclusively
uint256 d20Value = (randomness % 20) + 1;
// assign the transformed value to the address in the s_results mapping variable
s_results[s_rollers[requestId]] = d20Value;
// emitting event to signal that dice landed
emit DiceLanded(requestId, d20Value);
}
}
house functionFinally, the house function returns the house of an address.
To have a list of the house's names, create the getHouseName function that is called in the house function.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
contract VRFD20 is VRFConsumerBase, ConfirmedOwner(msg.sender) {
// ...
// { variables }
// ...
// ...
// { events }
// ...
// ...
// { constructor }
// ...
// ...
// { rollDice function }
// ...
// ...
// { fulfillRandomness function }
// ...
// house function
function house(address player) public view returns (string memory) {
// dice has not yet been rolled to this address
require(s_results[player] != 0, "Dice not rolled");
// not waiting for the result of a thrown die
require(s_results[player] != ROLL_IN_PROGRESS, "Roll in progress");
// returns the house name from the name list function
return getHouseName(s_results[player]);
}
// getHouseName function
function getHouseName(uint256 id) private pure returns (string memory) {
// array storing the list of house's names
string[20] memory houseNames = [
"Targaryen",
"Lannister",
"Stark",
"Tyrell",
"Baratheon",
"Martell",
"Tully",
"Bolton",
"Greyjoy",
"Arryn",
"Frey",
"Mormont",
"Tarley",
"Dayne",
"Umber",
"Valeryon",
"Manderly",
"Clegane",
"Glover",
"Karstark"
];
// returns the house name given an index
return houseNames[id.sub(1)];
}
}
You have now completed all necessary functions to generate randomness and assign the user a Game of Thrones house. We've added a few helper functions in there to make using the contract easier and more flexible. You can deploy and interact with the complete contract in Remix.
You will now deploy your completed contract. This deployment is slightly different than the example in the Deploy Your First Contract guide. In our case, you will have to pass in parameters to the constructor upon deployment.
Once compiled, you'll see a dropdown menu that looks like this in the deploy pane:

Select the VRFD20 contract or the name that you gave to your contract. You will deploy this contract on the Kovan test network.
Click the caret arrow on the right hand side of Deploy to expand the parameter fields, and paste the following values in:
0xdD3782915140c8f3b190B5D67eAc6dc5760C46E90xa36085f69e2889c224210f603d836748e7dc00880x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4100000000000000000
Then click the transact button.
These are the coordinator address, LINK address, key hash, and fee. For a full reference of the addresses, key hashes, and fees for each network, see VRF Contracts. Click deploy and use your Metamask account to confirm the transaction.
Note: You should have some Kovan ETH in your Metamask account to pay for the GAS.
At this point, your contract should be successfully deployed. However, it can't request anything yet since it doesn't own LINK. If you click rollDice with no LINK, the transaction will revert.
Because the contract is on testnet, as with Kovan ETH, you don't need to purchase real LINK. You can request and obtain Testnet LINK from a faucet.
Note: You should add the corresponding LINK token to your MetaMask account first:
If you encounter any issues, make sure to check you copied the address of the correct network:
Use your Metamask address on the Kovan network to request LINK and send 1 LINK to the contract address. Find this address in Remix under Deployed Contracts on the bottom left.

rollDice?After you open the deployed contract tab in the bottom left, the function buttons are available. Find rollDice and click the caret to expand the parameter fields. Enter your Metamask address, and click 'roll'.
You will have to wait a few minutes for your transaction to confirm and the response to be sent back. You can get your house by clicking the house function button with your address. Once the response has been sent back, you'll be assigned a Game of Thrones house!
You might notice that there are more functions listed than you originally built in the smart contract. These were inherited from the VRFConsumerBase and ConfirmedOwner smart contracts, which are used in the contract definition.
To read more about generating random numbers in Solidity, read our blog posts:
To explore more applications of Chainlink VRF, check out our other tutorials.