🎲Randomness

Introduction

The BOG RNG V2 allows smart contracts to get randomness on-chain in a verifiably secure fashion.

Hashes of the pending numbers are stored in the RNG contract and assigned to randomness requests in the initial call. The oracle will then callback the requesting contract with the random number which is verified against the stored hash to ensure the random number provided has not been tampered with based on the nature of the request.

This is a simple but secure method of providing random numbers on-chain which allows developers to easily integrate it into their project instead of trying to roll their own solution

The RNG oracle can take fees in either BOG or BNB, the base amount of callback gas available is 200K however this is fully customizable by the developers by sending additional BNB with the request which can allow for complex operations to be executed in the RNG callbacks.

Fees

Our RNG fees are pegged to fiat amounts to ensure you do not pay more if the token you are paying with increases in price.

We take payment for the fees in BNB.

Token

Token Amount

Fiat Amount

$BNB

Variable

$1.25

Gas

RNG callbacks allow for up to 200K of gas to be consumed by your contract. If you require more than this you can send BNB with your randomness request equal to the amount of extra gas you require.

For example: requestRandomness{value: EXTRA_GAS}();

The callback gas price is set at 5 Gwei so for an extra 100K of callback gas you will need to send 500K Gwei with your request.

RNG Oracle

Addresses

The Mainnet RNG uses a polling rate of < 1 second so you can expect almost instant callbacks, whereas the Testnet RNG uses a polling rate of ~60 seconds as we do not run our own testnet node so callback times are increased.

Network

Address

Mainnet

0xe308d2B81e543b21c8E1D0dF200965a7349eb1b7

BSC Testnet

0x0eCb31Afe9FE6a10f9A173843aE7957Df39D8236

Testnet

The BSC Testnet Oracle can take fees in either BNB or Testnet BOG

Testnet fees are fixed at either 0.01 Testnet BOG or 0.01 BNB.

You may mint Testnet BOG to yourself using the BscScan link below.

https://testnet.bscscan.com/address/0x3000d464e43cadc4970af1655b0c7c4205232d87#writeContract

Please note gas costs for transferring Testnet BOG may differ to Mainnet BOG as mainnet transactions also power other contracts such as the price oracle used for determining RNG fees.

Interface

/**
 * Your contract should implement this
 */
interface IReceivesBogRandV2 {
    function receiveRandomness(bytes32 hash, uint256 random) external;
}
/**
 * You should cast our oracle to this
 */
interface IBogRandOracleV2 {
    // Request randomness with fee in BOG
    function getBOGFee() external view returns (uint256);
    function requestRandomness() external payable returns (bytes32 assignedHash, uint256 requestID);
 
    // Request randomness with fee in BNB
    function getBNBFee() external view returns (uint256);
    function requestRandomnessBNBFee() external payable returns (bytes32 assignedHash, uint256 requestID);
   
    // Retrieve request details
    enum RequestState { REQUESTED, FILLED, CANCELLED }
    function getRequest(uint256 requestID) external view returns (RequestState state, bytes32 hash, address requester, uint256 gas, uint256 requestedBlock);
    function getRequest(bytes32 hash) external view returns (RequestState state, uint256 requestID, address requester, uint256 gas, uint256 requestedBlock);
    // Get request blocks to use with blockhash as hash seed
    function getRequestBlock(uint256 requestID) external view returns (uint256);
    function getRequestBlock(bytes32 hash) external view returns (uint256);
 
    // RNG backend functions
    function seed(bytes32 hash) external;
    function getNextRequest() external view returns (uint256 requestID);
    function fulfilRequest(uint256 requestID, uint256 random, bytes32 newHash) external;
    function cancelRequest(uint256 requestID, bytes32 newHash) external;
    function getFullHashReserves() external view returns (uint256);
    function getDepletedHashReserves() external view returns (uint256);
   
    // Events
    event Seeded(bytes32 hash);
    event RandomnessRequested(uint256 requestID, bytes32 hash);
    event RandomnessProvided(uint256 requestID, address requester, uint256 random);
    event RequestCancelled(uint256 requestID);
}

Example Implementation

CoinFlip: Contract

This is an example implementation of a coin flip game where the user submits a transaction to the contract with a 50% chance of losing their wager and a 50% chance of winning double their wager.

The coin flips are stored in a mapping to the hash they were assigned when requesting randomness from the oracle.

The receiveRandomness(...) callback function processes the result of the coinflip with the corresponding hash.

Storing each coinflip separately using the RNG hash as a unique identifier means it is possible to make the contract non blocking, eg. no need to lock up the contract when waiting for the randomness callback as many users can use it at once and all be guaranteed a uniquely fair result.

contract CoinFlip is IReceivesBogRandV2 {
    using SafeMath for uint256;
   
    IBogRandOracleV2 rng;
 
    struct Flip {
        address owner;
        uint256 wager;
        bool flipped;
        bool won;
    }
 
    mapping (bytes32 => Flip) flips;
 
    constructor (address oracle) payable {
        rng = IBogRandOracleV2(oracle);
    }
 
    // Store a new coin flip & request a randomness callback from the RNG oracle
    function flipCoin() external payable {
        uint256 fee = rng.getBNBFee();
        require(msg.value >= fee);
 
        (bytes32 hash, ) = rng.requestRandomnessBNBFee{value: fee}();
       
        uint256 wager = msg.value.sub(fee);
 
        flips[hash] = Flip(msg.sender, wager, false, false);
    }
 
    // RNG oracle callback function. Processes the coinflip with the assigned random hash
    function receiveRandomness(bytes32 hash, uint256 random) external override {
        require(msg.sender == address(rng));
        require(flips[hash].flipped == false);
 
        flips[hash].flipped = true;
 
        if(random % 2 == 0){
            flips[hash].won = true;
            payable(flips[hash].owner).transfer(flips[hash].wager * 2);
            emit Won();
        }else{
            flips[hash].won = false;
            emit Lose();
        }
    }
 
    event Won();
    event Lose()
}

CoinFlip: Frontend web3

This example shows how to submit a flipCoin() transaction with enough BNB to cover the RNG fees. The BNB fee allows for a small amount of price slippage to prevent transactions failing due to a change in the price of BNB.

// Transaction parameters
let gas = 300000;
let accounts = await web3.eth.getAccounts();
 
// Load contracts
let rngOracle = new web3.eth.Contract(RNG_ABI, RNG_ADDRESS);
let coinFlip = new web3.eth.Contract(COINFLIP_ABI, COINFLIP_ADDRESS);
 
// Retrieve latest RNG fee in BNB
let rngFee = await rngOracle.methods.getBNBFee().call();
 
// Send method to contract with enough BNB to cover RNG fee
await coinFlip.methods.flipCoin().send({from: accounts[0], gas: gas, value: rngFee});Security Considerations for Verifiable Randomness
  • You MUST check that the random number is ONLY being provided from the RNG oracle contract.

  • Once the randomness has been requested by your contract you should not allow any other interactions that may be able to change the outcome. For example if a random number is requested to determine the winner of a lottery, no more tickets should be able to be bought until after the random number has been provided back to the contract.

  • If multiple random numbers may be requested by your contract you must ensure your contract can not be exploited by miners who alter the order of the random number callback transactions. Please use the hash returned by the requestRandomness() functions to allow each request to be processed in a standalone / unique manner.

  • You may consider using the random number provided along with other data to produce a hash used for randomness as this will add another layer of security. You can use the getRequestBlock() to get the blockhash of a block after the initial request to use as another seed for example.

Last updated