Using Randomness in your Smart Contracts with Chainlink VRF

Using Randomness in your Smart Contracts with Chainlink VRF - thirdweb Guides

Chainlink VRF (Verifiable Random Function) is a random number generator (RNG) that is provably fair and verifiable. Chainlink VRF generates one or more random numbers or “words”, a computer science term for a unit of data, along with cryptographic proof which is published and verified on-chain, for each request for randomness. This enables smart contracts to have access to tamper-proof, verifiably random values.

Why are Verifiably Random Numbers Needed?

Due to the deterministic nature of computers, it is difficult to achieve true randomness. Instead, attempts to generate random numbers often rely on pseudorandom algorithms, which produce sequences of numbers that appear random, but are actually predetermined. In the context of blockchain technology, random numbers can be generated either by using the block hash, which may be subject to manipulation or by relying on off-chain sources of randomness, which could potentially be manipulated by the off-chain provider or by the system responsible for transporting data on-chain. The solution for smart contract developers building contracts that rely on randomness to be provably fair and equally uncertain for all contract participants is Chainlink VRF.

Chainlink VRF can be used for a range of different use cases to build reliable smart contracts for any application which requires unpredictable outcomes. Some examples include:

  • Unpredictable rewards in the form of player-reward loot boxes or packs
  • Integrating unpredictable scenarios into web3 games e.g. NFT asset attributes
  • Provable random duty assignment e.g. auditors, judges, or contributors
  • Creating random samples e.g. from surveys or votes  

How does it work?

Chainlink VRF utilizes a Chainlink decentralized oracle network (DON). A Chainlink DON is a collection of nodes or servers which run the Chainlink software and participate in the network. When a contract sends a request for randomness, a set of the participating oracles in the network are selected to perform the incoming request with each node responding and the resulting responses aggregated. An individual node cannot manipulate or supply biased answers as the cryptographic proof would fail, and thus, a compromised node would not return a result. The on-chain nature of Chainlink VRF ensures that participating nodes cannot withhold a request without being financially penalized and possibly removed from the network and thus denied being paid fees for their randomness.

Subscription Method

In this guide, we are going to be walking through and then using the Subscription method for requesting randomness. This requires a subscription account that is funded with LINK tokens which users can then connect multiple consuming contracts to. When a user's contract requests randomness, the transaction costs are calculated after the request is fulfilled and the balance of the subscription is deducted. The subscription method allows you to fund multiple contracts from a single subscription.

Create a Subscription

To use Chainlink VRF in your own project, we first need to create and fund a subscription to connect our consumer contract. The Subscription Manager interface allows you to create an account and pre-pay for VRF for multiple contracts without needing to manage multiple wallets across different systems and applications. For this example, we will be working on the Goerli test network so make sure that your wallet is connected to Goerli and that you have some GoerliETH test funds, which you can obtain from a faucet, before proceeding. You will also need some testnet LINK to fund your subscription, which you can find here. On the subscription manager page, copy the Key hash and the VRF Coordinator addresses as these will be used when deploying your consumer contract, and select Create Subscription

Create a subscription on Chainlink
Create a subscription on Chainlink

You will then be given the subscription address and click Create Subscription again and confirm the transaction. You will be prompted to Add Funds to your subscription, which you can top-up or do so later but, add 10 LINK to your subscription to ensure that the contract is adequately funded.  

When asked to add a consumer contract, click I'll do it later and your subscription will now have been created! Copy your subscription ID as you will need to use that for deploying your contract.

Copy subscription id from the dashboard
Copy the subscription id from the dashboard

Next, we need to create and add a consumer contract.

Create a VRF Compatible Consumer Contract

A consumer contract is a contract that requests a random number. To create your own VRF-compatible consumer contract head to this GitHub repository and create a local version of the code. This repository is able to be used with either Foundry or Hardhat depending on your preferences and includes the required lib, Node modules, and tests for both frameworks. For this example, we will be working with Foundry. This repository includes simple example contracts which implement the four Chainlink services: VRF, Automation, Any API, and Data Feeds. For this example, open the following consumer contract file:

contracts/RandomNumberConsumerV2.sol

Inside this file, you will notice a couple of interesting features. Firstly, there are two functions that need to be implemented for the contract to be VRF V2 compatible:

  • requestRandomWords - the function which requests randomness
  • fulfillRandomWords - callback function used by the VRFCoordinator to perform the request
/**
     * @notice Requests randomness
     * Assumes the subscription is funded sufficiently; "Words" refers to unit of data in Computer Science
     */
    function requestRandomWords() external onlyOwner {
        // Will revert if subscription is not set and funded.
        s_requestId = COORDINATOR.requestRandomWords(
            i_keyHash,
            i_subscriptionId,
            REQUEST_CONFIRMATIONS,
            CALLBACK_GAS_LIMIT,
            NUM_WORDS
        );
    }

    /**
     * @notice Callback function used by VRF Coordinator
     *
     * @param requestId - id of the request
     * @param randomWords - array of random values from VRF Coordinator
     */
    function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords)
        internal
        override
    {
        s_randomWords = randomWords;
        emit ReturnedRandomness(randomWords);
    }

    modifier onlyOwner() {
        require(msg.sender == s_owner);
        _;
    }

The modifier onlyOwner() is added to requestRandomWords to ensure that only the owner of the contract can call this function and request randomness. The function, therefore, needs to have a state visibility of external as it will only be called by the owner rather than from inside the contract itself, and restricting the visibility reduces gas fees - always a good thing!  

This contract also includes the following pre-defined request parameters which can be modified depending on the chain you are deploying to but for this example, you will only need to set the Subscription ID when deploying your contract

  • Key hash - the gas lane to use which specifies the maximum gas price to bump to
  • Callback gas limit - Depends on the number of requested values. Storing each value costs ~ 20,000 gas
  • Request confirmations - The number of block confirmations before returning the request
  • NUM_WORDS - the number of words required per request. We will be requesting 2 random words per request.

Deploying Your Contract

To deploy your contract, run the following command in your terminal:

npx thirdweb@latest deploy

You will next be prompted to select a framework, we will select Foundry and then select RandomNumberConsumerV2 as the contract to deploy.

Deploy the smart contract using thirdweb deploy
Deploy the smart contract using thirdweb deploy

This will bring up your Dashboard in a browser window to set the contract parameters and deploy your contract

Populate metadata of your contract
Populate metadata of your contract

Paste in your Subscription ID, VRFCoordinator, and key hash addresses that we saved earlier, make sure that you have selected Goerli as your Network and click Deploy Now which will trigger two transactions that you will need to sign.

Once these transactions have competed, your contract will show up in your Dashboard where you will be able to view the read and write functions on the contract. On the top left-hand side, under the contract name, copy the address of the contract.

dashboard for your contract
dashboard for your contract

Add the Consumer Contract

In order to make a request for randomness, we will need to add our contract as a consumer to our subscription. To do this, head over to the Subscription Manager and click Add consumer and paste in your contract address.

Add consumer in Chainlink Subscription Manager
Add consumer in Chainlink Subscription Manager

You are now ready to make a request for randomness!

Make a Request for Randomness

Head back over to your contract dashboard and click on the write function requestRandomWords, click Execute and sign the transaction. It can take some time for the request to be returned so check your Subscription ID to see the status of your request. You may be prompted to add more funds to your Subscription in order to perform the request as the estimated gas price is often far higher than the actual gas price. Once the request has been fulfilled, head back over to your contract on your Dashboard and click on s_randomWords which is the variable which will store your random value(s) from the request. Since we requested two values, there will be a value indexed at key 0 and key 1. Input either value for the key parameter and click Run and the output should look something like this:

You can now get verified and reliable random number using Chainlink VRF now!

Congratulations - you have successfully received a verified and reliable random number using Chainlink VRF!

The Takeaway

In this guide, we learned about Chainlink VRF, how it works & why it is necessary when using random numbers in our smart contracts. We have learned how to create and fund a subscription to manage our consumer contracts and we have created a VRF-compatible contract and then successfully made a request for randomness from this contract!

Well done if you made it all the way through this guide! We are excited to see all of the different ways you use Chainlkink VRF in your own projects so please share them in our Discord where you will also be able to chat directly with someone from the thirdweb team if you have any questions on this guide.

To view the source code for this guide, please visit the GitHub repository.