Skip to main content

How to use Verifiable Random Functions (VRFs) in Arbitrum

PUBLIC PREVIEW DOCUMENT

This document is currently in public preview and may change significantly as feedback is captured from readers like you. Click the Request an update button at the top of this document or join the Arbitrum Discord to share your feedback.

Community member contribution

The following document was contributed by @DeanPress. Give them a shoutout if you find it useful!

Verifiable Random Functions (VRFs) are cryptographic methods to generate random numbers in a way that is verifiable and unpredictable. That means anyone can verify that a given number was generated correctly, but no one can predict the output before it's generated, even if they know the input.

VRFs can offer a reliable source of randomness for decentralized apps (dApps), such as games (e.g. card dealing, XP gain, treasure opening, hit/dodge rates), generating NFT traits, and airdrops.

Randomizer VRF

Randomizer is a decentralized VRF protocol on Arbitrum. It uses native ETH for fee settlement, makes fast callbacks to smart contracts, and offers an npm module to bring real-time results to dApps.

Requesting a random number from Randomizer

Here’s an example of how to use the Randomizer VRF with your Arbitrum smart contract:

    interface IRandomizer {
function request(uint256 callbackGasLimit) external returns (uint256);
function request(uint256 callbackGasLimit, uint256 confirmations) external returns (uint256);
function clientWithdrawTo(address _to, uint256 _amount) external;
}

// Coinflip contract
contract CoinFlip {
// Using Randomizer's Arbitrum Goerli address
IRandomizer public randomizer = IRandomizer(0x923096Da90a3b60eb7E12723fA2E1547BA9236Bc);
address public owner;

// Stores each game to the player
mapping(uint256 => address) public flipToAddress;

// Events
event Win(address winner);
event Lose(address loser);

modifier onlyOwner() {
require(msg.sender == owner, "Sender is not owner");
_;
}

// The coin flip containing the random request
function flip() external returns (uint256) {
// Get the latest randomizer contract from the testnet proxy
// Request a random number from the randomizer contract (50k callback limit)
uint256 id = randomizer.request(50000);
// You can also do randomizer.request(50000, 20) to get a callback after 20 confirmations for increased finality security (you can do 1-40 confirmations).
// Store the flip ID and the player address
flipToAddress[id] = msg.sender;
// Return the flip ID
return id;
}

// Callback function called by the randomizer contract when the random value is generated
function randomizerCallback(uint256 _id, bytes32 _value) external {
//Callback can only be called by randomizer
require(msg.sender == address(randomizer), "Caller not Randomizer");
// Get the player address from the flip ID
address player = flipToAddress[_id];
// Convert the random bytes to a number between 0 and 99
uint256 random = uint256(_value) % 100;
// If the random number is less than 50, the player wins
if (random < 50) {
emit Win(player);
} else {
emit Lose(player);
}
}

// Allows the owner to withdraw their deposited randomizer funds
function randomizerWithdraw(uint256 amount) external onlyOwner {
randomizer.clientWithdrawTo(msg.sender, amount);
}
}

Randomizer contract addresses

  • Arbitrum One: 0x5b8bB80f2d72D0C85caB8fB169e8170A05C94bAF
  • Arbitrum Goerli Testnet: 0x923096Da90a3b60eb7E12723fA2E1547BA9236Bc

Full documentation

Refer to Randomizer's documentation for more examples and instructions on how to integrate common features like having the end-user pay the VRF fee, refunding users for overdraft fees, and real-time results.

Full dApp example

Refer to Randomizer's Coinflip game source code for a full-stack example dApp (front-end and smart contract) that integrates real-time results, user payments for fees, and user refunds for overdraft fees.