Pass VRF Costs to the End Users
This tutorial shows you how to identify the cost of specific VRF requests so that you can pass this cost to the end users of your application.
Objective
The VRF direct funding method calculates costs at request time. In this tutorial, you will use VRF direct funding and map VRF request IDs to their corresponding responses from the VRF service, along with the cost of each request.
Before you begin
Before you start this tutorial, complete the following items:
-
Install Foundry.
- Open a Terminal window and run:
curl -L https://foundry.paradigm.xyz | bash
- Follow the instructions in your terminal to add
foundryup
to your CLI. - Run
foundryup
. After successful installation, you should get output that contains a Foundry banner. After that, you should seefoundryup
installingforge
and other packages:foundryup: installing foundry (version nightly, tag nightly-74c03182d41c75c40e5a1c398aca9400305ff678) foundryup: downloading latest forge, cast, anvil, and chisel ########################################################################################################## 100.0% foundryup: downloading manpages ########################################################################################################## 100.0% foundryup: installed - forge 0.2.0 (74c0318 2023-09-14T00:27:34.321593000Z) foundryup: installed - cast 0.2.0 (74c0318 2023-09-14T00:27:34.059889000Z) foundryup: installed - anvil 0.2.0 (74c0318 2023-09-14T00:27:34.192518000Z) foundryup: installed - chisel 0.2.0 (74c0318 2023-09-14T00:27:33.928662000Z) foundryup: done!
- Foundry does not support Powershell or Cmd, so you will need to install and use WSL. (Git Bash for Windows also works with Foundry.)
- After installing WSL or Git Bash, open a terminal window and run:
curl -L https://foundry.paradigm.xyz | bash
- Follow the instructions in your terminal to add
foundryup
to your CLI. - Run
foundryup
. After successful installation, you should get output that contains a Foundry banner. After that, you should seefoundryup
installingforge
and other packages:foundryup: installing foundry (version nightly, tag nightly-74c03182d41c75c40e5a1c398aca9400305ff678) foundryup: downloading latest forge, cast, anvil, and chisel ########################################################################################################## 100.0% foundryup: downloading manpages ########################################################################################################## 100.0% foundryup: installed - forge 0.2.0 (74c0318 2023-09-14T00:27:34.321593000Z) foundryup: installed - cast 0.2.0 (74c0318 2023-09-14T00:27:34.059889000Z) foundryup: installed - anvil 0.2.0 (74c0318 2023-09-14T00:27:34.192518000Z) foundryup: installed - chisel 0.2.0 (74c0318 2023-09-14T00:27:33.928662000Z) foundryup: done!
- Open a Terminal window and run:
-
Install
make
if you do not already have it.- The Xcode command line tools include
make
. If you've previously installed Xcode, skip to step 2 to verify your installation. Otherwise, open a Terminal window and run:
Alternatively, if you prefer to use Homebrew, be aware that GNUxcode-select --install
make
is installed asgmake
. - Verify your
make
installation:
Ifmake
make
is installed successfully, you will get the following output:make: *** No targets specified or no makefile found. Stop.
- If you're using WSL, open an Ubuntu terminal and run:
sudo apt install make
- Edit your path variable to include
make
. - Verify your
make
installation:
Ifmake
make
is installed successfully, you will get the following output:make: *** No targets specified or no makefile found. Stop.
- The Xcode command line tools include
-
Run
git --version
to check yourgit
installation. You should see an output similar togit version x.x.x
. If not, install git. -
Get your Etherscan API key for contract verification. If you do not already have one:
-
Create an account with Infura or Alchemy to use as an RPC endpoint if you do not already have one. Alternatively, you can use your own testnet clients.
Setup steps
This tutorial requires several setup steps:
Clone the example repo
Clone the vrf-direct-funding-example
repo:
git clone git@github.com:smartcontractkit/vrf-direct-funding-example.git
This gives you an existing Foundry project to configure and use.
Configure the Foundry project
Foundry is an Ethereum application development toolkit that is used here to configure and deploy the contract. You need the following information:
- The RPC URL for your deployment network, using an RPC node provider such as Infura or Alchemy.
- The private key for your deployer account. If your deployer account is in MetaMask, export your private key from MetaMask.
- An Etherscan API key for contract verification.
-
Create an
.env
file at the root of thevrf-direct-funding-example/
directory:# Network RPCs export RPC_URL= # Private key for contract deployment export PRIVATE_KEY= # Explorer API key used to verify contracts export EXPLORER_KEY=
-
In your
.env
file, fill in these values:Parameter Description Example RPC_URL
The RPC URL for the network you want to deploy to. https://sepolia.infura.io/v3/your-api-key
PRIVATE_KEY
The private key of the account you want to deploy from. 0xabc123abc123abc123abc123abc123...
EXPLORER_KEY
The API key for Etherscan needed for contract verification. ABC123ABC123ABC123ABC123ABC123ABC1
Install dependencies
At the root of the vrf-direct-funding-example/
directory, run:
make install
This command will install the project dependencies, which include chainlink and openzepplin-contracts for secure smart contract development.
Deploy the contract
Foundry includes Forge, an Ethereum testing framework. The project's Makefile helps you invoke Forge with simple commands to start the deployment script.
At the root of the vrf-direct-funding-example/
directory, run this command to deploy the contract:
make deploy
Run the example
Test the contract as the end-user.
-
Open the LINK Token Contract write functions for your network in an explorer. For example, on Polygon Mumbai you can open the 0x0ed18e80aacede8cb52a6043976c255eff6fedbf contract.
-
Connect your wallet to the scanning app so you can run write functions.
-
Approve the deployed contract to spend LINK. Run the
approve
function with your deployed contract address and5000000000000000000
Juels (5 LINK) as variables. Click Write to run the function. Metamask asks you to approve the transaction. -
Open your deployed contract in the scanner.
-
On the Contract tab, open the Write Contract functions list for your deployed contract.
-
Again, connect your wallet to the scanner so you can run write functions on the deployed contract.
-
In the
requestRandomWords
function, click Write to initiate a request for randomness as an end-user. Confirm the transaction in MetaMask. -
If the transaction is successful, you will see a View your transaction button.
-
On the Read Contract tab for your deployed contract, view the
lastRequestId
value. -
Use this value to find the request status and view the random value.
Code example
This section explains how the VRFDirectFundingConsumer.sol
contract maps each request to its cost.
This RequestStatus
struct is used to map attributes of each VRF request to its request ID, including its cost, its fulfillment status, and the random values generated by the VRF service in response to the request:
struct RequestStatus {
uint256 paid;
bool fulfilled;
uint256[] randomWords;
}
mapping(uint256 => RequestStatus) public s_requests;
The requestRandomWords()
function uses a method in the VRF wrapper contract to calculate the request cost upfront, and then transfers enough LINK to the VRF wrapper to cover the request. After sending the request, it sets up the RequestStatus
with the cost and other attributes to be updated after request fulfillment.
uint256 requestPrice = vrfWrapper.calculateRequestPrice(
requestConfig.callbackGasLimit
);
require(
linkTokenContract.transferFrom(
msg.sender,
address(this),
requestPrice
),
"Not enough LINK"
);
...
s_requests[requestId] = RequestStatus({
paid: requestPrice,
randomWords: new uint256[](0),
fulfilled: false
});
After the VRF service fulfills your randomness request, it calls back your fulfillRandomWords()
function, where you implement logic to handle the random values. It's recommended to keep the processing in your callback function minimal to save on gas costs. In this case, the example contract updates the RequestStatus
in the request mapping, and emits an event indicating that the request has been fulfilled. Another part of your application can listen for this event and further process the random values separately from the callback function.
function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override {
require(s_requests[_requestId].paid > 0, "request not found");
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords = _randomWords;
emit RequestFulfilled(_requestId, _randomWords, s_requests[_requestId].paid);
}