Access the oracle

For WASM-based implementation please refer to:

Access WASM random oracle

Using the DIA Randomness Oracle

DIA uses the drand.love public randomness beacon, and updates its oracle with round number, randomness and signature. Anyone can access published random values via round ID.

{
	"round": 1597683,
	"randomness": "24138936fcbf7fc3951c928158be6998cee3af622142d0790333608d17a5c5f6",
	"signature": "8c04905c0adf34f1fb007915d9ccc7d07b97305fc63952726f9367c87f36ab687c5e190c151f6ac4d760a9e009fc54230adb8513885449d649a229bc727be9ff347bdbce1c609cebf993b6ae57133fbcf23f96b15dbd3510cb5f2ade6b30b647",
	"previous_signature": "ada42197a2db89866da4c44348f77f7868e41e961ec32e636b912d43c625386afae9e54944ac573047dbd227ee495b52059586c8d8cd0edfe18cc15ca0666a66651da1d62b12af2d0fac19735bed9298690a593571965c3ad7c7b11947e76ec0"
}

The DIA randomness smart contract is structured as follows

pragma solidity ^0.8.0;

contract DIARandomOracle {

  struct Random { 
    string randomness;
    string signature;
    string previousSignature;
  }

  mapping (uint256 => Random) public values;
  uint256 public lastRound = 0;

  address public oracleUpdater;

  event OracleUpdate(string key, uint128 value, uint128 timestamp);
  event UpdaterAddressChange(address newUpdater);

  constructor() {
    oracleUpdater = msg.sender;
  }

  function setRandomValue(uint256 _round, string memory _randomness, string memory _signature, string memory _previousSignature) public {
    require(msg.sender == oracleUpdater,"not a updater");
    require(lastRound<_round, "old round");
    lastRound = _round;
    values[_round] = Random(_randomness, _signature, _previousSignature);
  }

  function getValue(uint256 _round) external view returns (Random memory) {
    return values[_round];
  }

  function updateOracleUpdaterAddress(address newOracleUpdaterAddress) public {
    require(msg.sender == oracleUpdater,"not a updater");
    oracleUpdater = newOracleUpdaterAddress;
    emit UpdaterAddressChange(newOracleUpdaterAddress);
  }

  function getRandomValueFromRound(uint256 _round) external view returns (string memory) {
    return values[_round].randomness;
  }

  function getRandomValueFromRoundWithSignature(uint256 _round) external view returns (Random memory) {
    return values[_round];
  }

  function getLastRound() public view returns(uint256) {
    return lastRound;
  }
}

Users can call getLastRound()to obtain the ID of the latest published round. To obtain the randomness of a certain round, users can call getRandomValueFromRound(uint256 _round)using the obtaines round ID.

The signature can also be requested by calling getRandomValueFromRoundWithSignature(uint256 _round).

Please be aware that you should always let all inputs commit before any randomness is used in a later round. For example, if you build a lottery, only call randomness after the last participant has committed their stake. To show this in an example, we will build a simple dice game.

Example: Dice Game

Imagine a simple game where two players play against each other. Both throw a dice and the user with the higher rolled number wins. In case of a draw, nobody wins. Both players need to "seed" the game with an initial number between 1 and 6, that will be added to the randomness (modulo 6). It is a fair game where everyone has a 50% chance of winning.

pragma solidity ^0.8.0;
  

import "../DIARandomOracle.sol";

contract DiceGame {
  address public randomOracle;
  uint256 seed1 = 0;
  uint256 seed2 = 0;
  uint256 latestRoundId = 0;
  
  constructor(address oracle) {
    randomOracle = oracle;
  }

  function getRandomValue(uint256 _round) public view returns (string memory) {
    return DIARandomOracle(randomOracle).getRandomValueFromRound(_round);
  }
  
  function commitPlayer1(uint256 seed) external {
    require(seed > 0, "Seed must be between 1 and 6");
    require(seed < 7, "Seed must be between 1 and 6");
    
    seed1 = seed;
    latestRoundId = DIARandomOracle(randomOracle).getLastRound();
  }
  
  function commitPlayer2(uint256 seed) external {
    require(seed > 0, "Seed must be between 1 and 6");
    require(seed < 7, "Seed must be between 1 and 6");
    
    seed2 = seed;
    latestRoundId = DIARandomOracle(randomOracle).getLastRound();
  }
  
  function rollDice() public view returns (uint) {
    require(seed1 > 0, "Player 1 needs to commit their seed!");
    require(seed2 > 0, "Player 2 needs to commit their seed!");
    
    uint _round = latestRoundId + 10;
    
    require(DIARandomOracle(randomOracle).getLastRound() >= _round, "Wait for the randmoness round to roll your dice.");
    
    string memory rand = getRandomValue(_round);
    uint256 player1result = (uint256(keccak256(abi.encodePacked(rand))) + seed1) % 6;
    uint256 player2result = (uint256(keccak256(abi.encodePacked(rand))) + seed2) % 6;
    
    seed1 = 0;
    seed2 = 0;
    
    if (player1result > player2result) {
      return 1;
    } else if (player2result > player1result) {
      return 2;
    } else {
      return 0;
    }
  }
}

Both players need to commit their seeds. The latest published roundID is stored for the duration of the game. When both committed their values and the wait time of 10 rounds has passed, the dice can be rolled and the player ID of the winner is returned (or 0 in case of a draw).

Make sure to never directly query the latest randomness value, otherwise the miner and the randomness oracle can interfere with the result. Always commit values before the randomness is used in a later round.

Last updated