Smart Contract Development
  • Introduction
    • What is a Transaction
    • Accounts and Signing
    • What is a smart contract
  • Learning Solidity
    • Introduction
    • Module 1
      • Variable Types
      • Variable Scope: State & Local variables
      • Global variables
      • Functions
        • View and Pure
        • Shadowing in Fuctions
      • Mapping
      • Require
      • Events
    • Project #1: Simple Registry
    • Module 2
      • Constructor
      • Data Location: Value & Reference
      • Interface
      • Import
        • Importing in Foundry
      • Inheritance
      • ERC-20
      • Checks-effect-interaction pattern
    • Project #2: Basic Vault
    • Module 3
      • Payable
      • Receive
      • Fallback
      • Returns
    • Project #3: ERC20+ETH Wrapper
    • Module 4
      • Immutable and Constant
      • Fixed-point Math
      • Abstract contracts
      • ERC-4626
      • Modifier + Inheritance +Ownable
      • Type
    • Project #4: Fractional Wrapper
    • Module 5
      • If-else
      • Libraries
        • TransferHelper
      • Chainlink Oracle
    • Project #5: Collateralized Vault
  • Compendium
    • Solidity Basics
      • Variable Types
      • Value Types
        • address
        • enum
      • Reference Types
        • strings
        • mappings
        • struct
        • Arrays
        • Multi-Dimensional arrays
      • Global Objects
      • Functions
        • Function types
        • Constructor Function
        • Transaction vs Call
        • Require, Revert, Assert
      • Function signature + selectors
      • Payable
        • Payable + withdraw
        • msg.value & payable functions
      • Receive
      • Fallback function (sol v 0.8)
        • Fallback function (sol v 0.6)
      • call, staticcall, delegatecall
    • Return & Events
    • Control Variable Visibility
    • Local Variables (Storage v Memory)
    • Data Location and Assignment Behaviors
    • Modifiers & Inheritance & Import
      • import styles
    • Interface & Abstract Contracts
    • ABI & Debugging
    • Libraries
    • Conditional(ternary) operators
    • Smart Contract Life-cycle
      • Pausing Smart Contracts
      • Destroying Smart Contracts
    • Merkle Trie and MPT
    • Merkle Tree Airdrop
  • Try & catch
  • Ethereum Signatures
  • EVM, Storage, Opcodes
    • EVM
    • Wei, Ether, Gas
    • Storage
    • ByteCode and Opcodes
    • Transaction costs & Execution costs
  • Reading txn input data
  • Data Representation
  • Yul
    • Yul
      • Intro
      • Basic operations
      • Storage Slots
      • Storage of Arrays and Mappings
      • Memory Operations
      • Memory: how solidity uses memory
      • Memory: Return, Require, Tuples and Keccak256
      • Memory: Logs and Events
      • Inter-contract calls
      • calldata
      • free memory pointer
    • Yul Exercises
      • read state variable
      • read mapping
      • iterate Array, Return Sum
    • memory-safe
  • Upgradable Contracts
    • Upgradability & Proxies
    • UUPS Example
    • Minimal Proxy Example
    • TPP Example
    • 🚧Diamond
      • On Storage
  • Gas Opt
    • Block Limit
    • gasLimit & min cost
    • Solidity Optimiser
    • Memory v calldata
    • Memory caching vs direct storage vs pointers
    • < vs <=
    • reverting early
    • X && Y, ||
    • constant and immutable
    • caching sload into mload
    • Syntactic Sugar
    • using unchecked w/o require
    • Compact Strings
    • Calling a view function
    • Custom errors over require
    • usage of this.
      • multiple address(this)
  • ERCs & EIPs
    • ERC-20.sol
      • Core functions
      • transfer()
      • transferFrom()
      • TLDR transfer vs transferFrom
    • Landing
      • ERC721.sol
      • EIP-721
        • LooksRare
        • Page 1
      • ERC-1271
      • EIP-2981
      • ERC-165
      • EIP-1167: Minimal Proxy Contract
    • VRFConsumerBase
    • UniswapV2Library
  • Yield Mentorship 2022
    • Projects
      • #1 Simple Registry
      • #2 Basic Vault
      • #3 ERC20+ETH Wrapper
        • setFailTransferTrue
      • #4 Fractional Wrapper
      • #5 Collateralized Vault
        • Process
        • Vault.sol
        • Testing
        • Chainlink Oracles
        • Pricing + Decimal scaling
        • Refactor for Simplicity
      • #9 Flash Loan Vault
        • Implementing ERC3156
        • Full code for lender
        • Ex-rate calculation
    • State Inheritance Testing
    • Testing w/ Mocks
    • Yield Style Guide
    • Github Actions
    • TransferHelper.sol
    • math logic + internal fn
    • Interfaces: IERC20
  • Foundry
    • Overview
    • Importing Contracts
    • Testing
      • stdError.arithmeticError
      • assume vs bound
      • Traces
      • label & console2
      • std-storage
  • Smart Contract Security
    • Damn Vulnerable Defi
      • 1. Unstoppable
      • 2. Naive receiver
      • 3. Truster
      • 4. Side Entrance
      • 5. The Rewarder
      • 6. Selfie
      • 7. Compromised
      • 8. Puppet
      • 9. Puppet V2
      • 10 - Free Rider
    • Merkle Tree: shortened proof attack
  • Fixed-Point Math
    • AMM Math
  • Solidity Patterns
    • checks-effects-interactions pattern
    • Router // batch
    • claimDelegate: stack unique owners
    • claimDelegate: cache previous user
  • Array: dup/ascending check
  • Deployment
    • Behind the Scenes
    • Interacting with External Contracts
    • Logging, Events, Solidity, Bloom Filter
  • Misc
    • Mnemonic Phrases
    • Bidul Ideas
  • Archive
    • Brownie Framework
      • Brownie basics
        • storing wallets in .env
        • Deployment to ganache
        • Interacting with contract
        • Unit Testing
        • Testnet deployment
        • Interacting w/ deployed contract
        • Brownie console
      • Brownie Advanced
        • Dependencies: import contracts
        • helpful_scripts.py
        • verify and publish
        • Forking and Mocking
        • Mocking
        • Forking
      • Testing
      • Scripts Framework
        • deploy.py
        • get_accounts
        • deploy_mocks()
        • fund_with_<token>()
      • Brownie Networks
    • Brownie Projects
      • SharedWallet
        • Multiple Beneficiaries
        • Common Code Contract
        • Adding Events
        • Renounce Ownership
        • Separate Files
      • Supply Chain
        • ItemManager()
        • Adding Events
        • Adding unique address to each item
      • Lottery
      • Aave - Lending and Borrowing
        • Approve & Deposit
        • Borrow
      • NFT
      • Advanced Collectible
        • adv_deploy() + Testing
        • Create Metadata
        • Setting the TokenURI
    • node npm
    • Ganache
    • Truffle
    • Remix
    • Installing Env
Powered by GitBook
On this page
  • Problem: RNG generation
  • Solution
  • Application
  • Request randomness
  • fulfillRandomness
  • requestRandomness() mechanics
  • Function execution breakdown
  1. ERCs & EIPs

VRFConsumerBase

VRF v1

PreviousEIP-1167: Minimal Proxy ContractNextUniswapV2Library

Last updated 3 years ago

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

  • 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.

  • 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.

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

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 .

VRFConsumerBase
VRF Contracts page
VRFConsumerBase constructor
results in fulfillRandomness being called
VRFConsumerBase.sol
LinkTokenInterface.sol