Search⌘ K

Writing and Deploying a VRF Consumer Contract

Understand how to create and deploy a VRF consumer smart contract in Solidity that interacts with Chainlink's VRF to generate verified random values. Learn to set up contract inheritance, configure subscription IDs, request random numbers, and retrieve them securely for use in Play-to-Earn blockchain games.

In this example, we'll request a random dice result as a uint between 1 and 6 from the VRF.

Chainlink provides a sample consumer contract of the VRF, which we can take as a starting point and modify for our purpose.

Solidity
// SPDX-License-Identifier: MIT
// An example of a consumer contract that relies on a subscription for funding.
pragma solidity ^0.8.7;
import '@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol';
import '@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol';
import '@chainlink/contracts/src/v0.8/ConfirmedOwner.sol';
/**
* Request testnet LINK and ETH here: https://faucets.chain.link/
* Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
*/
/**
* THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
* DO NOT USE THIS CODE IN PRODUCTION.
*/
contract VRFv2Consumer is VRFConsumerBaseV2, ConfirmedOwner {
event RequestSent(uint256 requestId, uint32 numWords);
event RequestFulfilled(uint256 requestId, uint256[] randomWords);
struct RequestStatus {
bool fulfilled; // whether the request has been successfully fulfilled
bool exists; // whether a requestId exists
uint256[] randomWords;
}
mapping(uint256 => RequestStatus) public s_requests; /* requestId --> requestStatus */
VRFCoordinatorV2Interface COORDINATOR;
// Your subscription ID.
uint64 s_subscriptionId;
// past requests Id.
uint256[] public requestIds;
uint256 public lastRequestId;
// The gas lane to use, which specifies the maximum gas price to bump to.
// For a list of available gas lanes on each network,
// see https://docs.chain.link/docs/vrf/v2/subscription/supported-networks/#configurations
bytes32 keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;
// Depends on the number of requested values that you want sent to the
// fulfillRandomWords() function. Storing each word costs about 20,000 gas,
// so 100,000 is a safe default for this example contract. Test and adjust
// this limit based on the network that you select, the size of the request,
// and the processing of the callback request in the fulfillRandomWords()
// function.
uint32 callbackGasLimit = 100000;
// The default is 3, but you can set this higher.
uint16 requestConfirmations = 3;
// For this example, retrieve 1 random value in one request.
// Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS.
uint32 numWords = 1;
/**
* HARDCODED FOR GOERLI
* COORDINATOR: 0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D
*/
constructor(uint64 subscriptionId)
VRFConsumerBaseV2(0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D)
ConfirmedOwner(msg.sender)
{
COORDINATOR = VRFCoordinatorV2Interface(0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D);
s_subscriptionId = subscriptionId;
}
// Assumes the subscription is funded sufficiently.
function requestRandomWords() external onlyOwner returns (uint256 requestId) {
// Will revert if subscription is not set and funded.
requestId = COORDINATOR.requestRandomWords(
keyHash,
s_subscriptionId,
requestConfirmations,
callbackGasLimit,
numWords
);
s_requests[requestId] = RequestStatus({randomWords: new uint256[](0), exists: true, fulfilled: false});
requestIds.push(requestId);
lastRequestId = requestId;
emit RequestSent(requestId, numWords);
return requestId;
}
function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override {
require(s_requests[_requestId].exists, 'request not found');
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords[0] = (_randomWords[0]%6)+1;
emit RequestFulfilled(_requestId, _randomWords);
}
function getRequestStatus(uint256 _requestId) external view returns (bool fulfilled, uint256[] memory randomWords) {
require(s_requests[_requestId].exists, 'request not found');
RequestStatus memory request = s_requests[_requestId];
return (request.fulfilled, request.randomWords);
}
}

Setting up the contract

A consumer contract inherits from the VRFCoordinatorV2Interface.sol interface, an abstract VRFConsumerBaseV2.sol contract, and a ConfirmedOwner.sol contract, which we import in lines 5–7. Moreover, in line 30, we declare COORDINATOR, a VRFCoordinatorV2Interface object.

These base contracts and interfaces allow our contract to talk to Chainlink's VRF.

To understand what they do, let's first take a look at the constructor of our derived contract in lines 33–69:

  • Upon deploying the contract, we must provide a uint64 variable or state ...