Deploying VRF Coordinator V2 Mock for Local Blockchain Environments

Peter M. Ogwara
Coinmonks
5 min readAug 28, 2022

--

Recently I spent hours trying to use the new version of Chainlink’s very helpful random number generator, VRFCoordinatorV2Mock, from Patrick Collins’s comprehensive blockchain tutorial on Youtube which, unfortunatately, uses the old version. While the actual implementation is quite easy, figuring out exactly what to do differently can take a bit of mental power and valuable time.

Here’s a step-by-step guide to implementing VRFCoordinatorV2Mock in your local blockchain environment.

Prerequisites

For this writeup, you’ll need a basic knowledge of python and some solidity, and the necessary tools to write and compile code in both languages, such as VSCode. You’ll also need to have installed ganache, and the python pip packages solcx and brownie, and included the chainlink dependencies in your brownie-config.yaml file. Get the VRF Coordinator V2 Mock from Chainlink’s github repository here and include the file in your contracts/test folder.

Essentially, this guide assumes you’ve either made use of the previous version of Chainlink’s VRFCoordinator or followed Patrick Collin’s tutorial up to the Smart Contract Lottery section.

Step 1: Deploy VRF Coordinator V2 Mock

Unlike the first version, VRFCoordinatorV2Mock takes two arguments, _baseFee and _gasPriceLink, when being deployed. _baseFee refers to the fee or premium paid to the oracle to use the contract and is different for each blockchain. You can find a full list here. _gasPriceLink is the gas price for sending transactions which is typically some value set dynamically based on the price of the layer on which the transaction is being deployed.

To get your Mock Contract to work, set _baseFee to 25000000000000000 (this corresponds to 0.25LINK) and _gasPriceLink to 1000000000. However, if the mock refuses to generate the random numbers (referred to as randomWords), feel free to add extra zeros to _gasPriceLink. This typically depends on the size of the contract.

Thus, you can deploy and ensure the VRF Coordinator mock contract will be deployed whenever you’re on a local blockchain by incuding the code below in your deploy.py script (or whatever python script you run your code through).

from brownie import network, accounts, VRFCoordinatorV2Mock LOCAL_BLOCKCHAIN_ENVIRONMENTS = [“development”, “ganache-local”] if(network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS): account = accounts[0] VRF_contract = VRFCoordinatorV2Mock.deploy(25000000000000000, 1000000000, {“from”: account})

Step 2: Create and Fund a local VRF Subscription

Next, you must create a VRF Subscription to use the contract. On the mainnet this may be done at the VRF Subscription manager. However, on our local Mock contract, you must call the methods yourself. The createSubscription method takes no arguments and returns nothing. The ID of the subscription created can be gotten by accessing a “SubscriptionCreated” event. With this ID, the subscription must then be funded with an arbitrary amount of LINK higher than the previously set _baseFee, by calling the fundSubscription method. This takes two arguments: _subID (the subscription ID) and _amount (the amount of LINK to fund with). I recommend funding with at least 3000000000000000000 (3 LINK).

sub_id_txn = VRF_contract.createSubscription({“from”: account}) sub_id_txn.wait(1) sub_id = sub_id_txn.events[“SubscriptionCreated”][“subId”] fund_amount_link = 3000000000000000000 fund_vrf_txn = VRF_contract.fundSubscription(sub_id, fund_amount_link, {“from”: account})

Step 3: Make a Request

Make a request to the VRF Coordinator Mock. This can be carried out by calling the requestRandomWords method either directly or through your contract. Either way, the function takes five arguments, so care must be taken to get them right. Argument 1 is _keyhash. On the mainnet, this would differ based on the network and can be found Here. In development the keyhash of any network would do fine. Argument 2 is _subId, which we already generated above. Argument 3 is _minimumRequestConfirmations, which could be any number but recommended to be set as 3. Argument 4 is _callbackGasLimit, recommended by Chainlink to be set as 100000. However, in the example below, I added an extra zero. This will ensure we don’t get reverted on gas estimation error. Argument 5 is _numWords, which tells the contract how many random numbers to generate. Unlike the first version, VRFCoordinatorV2Mock can generate more than one random number at a time.

The requestRandomWords method emits a unique requestID which can be accessed from a RandomWordsRequested event.

keyhash = “0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15” #goerli_keyhash minReqConf = 3 #MinimumRequestConfirmations gasLim = 1000000 #callbackGasLimit numwords = 1 make_request_txn = VRF_contract.requestRandomWords(keyhash, sub_id, minReqConf, gasLim, numwords, {“from”: account}) make_request_txn.wait(1) requestID = make_request_txn.events[“RandomWordsRequested”][“requestId”]

Step 4: Fulfill the Request

The final step will require the address of your deployed contract. This is because VRFCoordinatorV2Mock works by sending the generated words to your contract through your contract’s fulfillRandomWords method. Your contract must have this method to receive the random numbers.

Fulfill the request by calling VRFCoordinatorV2Mock’s fulfillRandomWords function. This takes two arguments: _requestId, the ID of the request generated above and _consumer, the address of your contract. The success of the fulfillment may be known by accessing the “success” index of the emitted event “RandomWordsFulfilled”. If true, the words have been sent to your contract successfully. If false, you may need to tweak your gas and funding figures a little bit more or confirm that the address of your contract is correct.

contract = ExampleContract.deploy(VRF_contract.address, {“from”: account}) #Change this line to YOUR contract fulfill_txn = VRF_contract.fulfillRandomWords(requestID, contract.address, {“from”: account}) fulfill_txn.wait(1) success = fulfill_txn.events[“RandomWordsFulfilled”][“success”] print(f”Success is {success}”) if(success): randomWord = contract.s_randomWords(0) print(f”Random number is {randomWord}”)
Calling fulfillRandomWords
Solidity code for ExampleContract

Remember: The generated words are an array, and such each must be accessed using the appropriate index, such as s_randomwords[0], s_randomwords[1], etc.

Notes:

> Mock Contracts imitate real contracts already deployed on a blockchain mainnet in your local development environment. This makes it possible to run local tests without always having to interact with the mainnet, which is a much slower process.

> In the example above, wait(1) is called after each transaction to ensure such would have completed before using values from it. While not absolutely necessary, it is recommended, especially on a mainnet.

Feel free to ask questions!

Follow me for more Blockchain, Javascript, PHP, Python or pure programming stories.

Medium| Twitter | Github | Amazon

New to trading? Try crypto trading bots or copy trading

--

--

Peter M. Ogwara
Coinmonks

Academic Writer, Full-Stack Web Developer, Web3 Enthusiast