VRFConsumerBase

VRF v1

Problem: RNG generation

RNG cannot be done on-chain the blockchain is deterministic -> all nodes must come to the same consensus.

Solution

Off-chain RNG, but verifiable on-chain.

  1. Smart contracts send requests for randomness

  2. Requests are sent to a VRF Coordinator, which is another contract on-chain

  3. The VRF Coordinator hands this request to an off-chain chainlink oracle for RNG.

  4. After RNG, VRF Coordinator verifies the randomness of the result on-chain.

Application

To consume randomness we first need to import and inherit from VRFConsumerBase and define two required functions:

  • requestRandomness: Make a request to the VRFCoordinator.

  • fulfillRandomness: Called by VRFCoordinator when it receives a valid VRF proof.

Your contract should own enough LINK to pay the specified fee.

Note, the below values have to be configured correctly for VRF requests to work. You can find the respective values for your network in the VRF Contracts page.

  • LINK Token - LINK token address on the corresponding network (Ethereum, Polygon, BSC, etc)

  • VRF Coordinator - address of the Chainlink VRF Coordinator

  • Key Hash - public key against which randomness is generated

  • Fee - fee required to fulfill a VRF request

On your contract:

import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";

contract MyContract is VRFConsumerBase {
    
    bytes32 internal keyHash;
    uint256 internal fee;
    
    uint256 public randomResult;
    
/**
     * Constructor inherits VRFConsumerBase constructor
     * 
     * Network: Kovan
     * Chainlink VRF Coordinator address: 0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9
     * LINK token address:                0xa36085F69e2889c224210F603D836748e7dC0088
     * Key Hash: 0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4
     */
    constructor() 
        VRFConsumerBase(
            0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9, // VRF Coordinator
            0xa36085F69e2889c224210F603D836748e7dC0088  // LINK Token
        )
    {
        keyHash = 0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4;
        fee = 0.1 * 10 ** 18; // 0.1 LINK (Varies by network)
    }
    
    // Requests randomness  
    function getRandomNumber() public returns (bytes32 requestId) {
        require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
        return requestRandomness(keyHash, fee);
    }
    // Callback function used by VRF Coordinator
    function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
        randomResult = randomness;
    }

MyContract inherits VRFConsumerBase, and therefore its constructor as well.

VRFConsumerBase constructor requires two inputs,

  • _vrfCoordinator -> address of the VRF coordinator contract on the chain we are deploying to.

  • _link -> address of the Link token contract on the chain.

VRF Coordinator is a smart contract that receives requests, hands them off-chain, and subsequently verifies randomness of the generated number on-chain.

The random number generation is done off-chain via chainlink's oracle network.

Request randomness

    function getRandomNumber() public returns (bytes32 requestId) {
        require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
        return requestRandomness(keyHash, fee);
    }    

In MyContract, getRandomNumber() calls on requestRandomness(keyHash, fee). This has been imported from VRFConsumerbase.sol

  • pass the keyhash of oracle node

  • pass the fee for request

The return requestRandomness(keyHash, fee) will emit a log to the chainlink oracle we have specified with keyHash.

The oracle will look for this request, generate a random number. This is then returned on-chain by VRF coordinator.

seed is deprecated and no longer used. It is kept for backward-compatibility. See VRFConsumerBase.sol for notes.

In your implementation of getRandomNumber() or whichever function that calls on requestRandomness, make sure your contract holds the necessary LINK to successfully call requestRandomness. Else gas estimation error will occur.

fulfillRandomness

Callback function used by VRF Coordinator to return the RNG after on-chain verification.

function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
    randomResult = randomness;
}

VRF will call this, by passing the requestID and randomness. We will catch the RNG and store it into randomResult.

Techincally, VRFConsumerBase calls rawFulfillRandomness() on verification:

This ensures that only VRF coordinator can respond and call fulfillRandomness, to prevent spoofed responses.

Also fulfillRandomness is internal, therefore can only be called by rawFulfillRandomness, and not other external functions.

VRF Coordinator looks for the function signature of fulfillRandomness on our contract and calls it. Which is why we must inherit this function from VRFConsumerBase and override it to suit our needs.

** Function signature is the hash of the function name.

requestRandomness() mechanics

requestRandomness() will call transferAndCall() through the LINK interface

transferAndCall() originates from the LINK token contract, and is interacted with through an interface that was imported: LinkTokenInterface.sol

in VRFConsumerBase.sol import "./interfaces/LinkTokenInterface.sol"; (state) LinkTokenInterface immutable internal LINK (constructor) LINK = LinkTokenInterface(_link);

transferAndCall sends our link tokens as fee and will call on the coordinator we specified.

Function execution breakdown

  • Smart contracts send requests for randomness

  • Requests sent to VRF Coordinator contract, via requestRandomness(keyHash, fee, userprovidedSeed) inherited from VRFConsumerBase.sol

  • VRF Coordinator hands this request to an off-chain chainlink oracle for RNG.

  • After RNG, VRF Coordinator verifies the randomness of the result on-chain.

Last updated