Harnessing DIA Oracles for Verifiable Randomness on CrossFiChain


INTRODUCTION
Welcome to this series on building the King of the Hill smart contract! In this tutorial, we will leverage Dia oracles to generate randomness, which will play a crucial role in determining the next King of the Hill.
CodeBase Repository:
You can find the complete codebase for this project in the following repository:
OverView
The King of the Hill smart contract is a blockchain-based game where users input a number (1-100). If the number they provide is evenly divisible by a randomly generated number from Dia oracles, they become the new King of the Hill
Setting Up Your Codebase
Before diving into smart contract development, ensure that your development environment is set up correctly. If you haven’t done so yet, refer to our previous article for a step-by-step guide:
Smart contract Development
The King of the Hill Smart Contract
The King of the Hill contract will consist of the following key components:
Integration with Dia Oracles – Fetching randomness from Dia.
User Input Validation – Ensuring inputs meet the required conditions.
Divisibility Check – Determining if the input number qualifies the user as the new King.
Updating the Throne – Assigning the user as the new King of the Hill if the conditions are met.
Step 1: Integrating with Dia Oracles
To retrieve Randomness number from Dia oracles we used their smart contract interface which facilitates getting the random number from their oracles:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0 ;
interface IDIAOracle {
struct Random {
string randomness;
string signature;
string previousSignature;
}
function setRandomValue(uint256 _round, string memory _randomness, string memory _signature, string memory _previousSignature) external;
function getValue(uint256 _round) external view returns (Random memory);
function updateOracleUpdaterAddress(address newOracleUpdaterAddress)external;
function getRandomValueFromRound(uint256 _round) external view returns (string memory);
function getRandomValueFromRoundWithSignature(uint256 _round) external view returns (Random memory);
function getLastRound() external view returns(uint256);
}
Which in our case , create a new folder under the src folder and name it interface inside the folder create a file “IDia_Oracle.sol” and copy the above codeblock.
Now, Having the interface create another file King_of_the_hill.sol inside src folder and import the interface.
Step 2: Implementing User validation
require(guessNumber > 0,"guessNumber must be greater than 0");
require(guessNumber <= 100,"guessNumber must be less than or equal to 100");
we ensure user inputs follows our predefined set of rules where the number should be greater than Zero and less or equal to 100;
Step 3: Checking Divisibility
To determine if a user can claim the throne, we check if their number is divisible by the random number retrieved.
//check if is divible by the number
function isDivible(uint256 _guess,uint256 _random_number)public pure returns(bool){
return _random_number % _guess == 0;
}
Step 4: Updating the King
If the user's number is valid and divisible by the random number, they become the new King of the Hill.
//claimThrone
function claimThrone(uint256 guessNumber)external{
require(guessNumber > 0,"guessNumber must be greater than 0");
require(guessNumber <= 100,"guessNumber must be less than or equal to 100");
uint256 generatedRandom = getRandomValue();
bool is_divible =isDivible(guessNumber, generatedRandom);
if (is_divible) {
king_of_the_hill = msg.sender;
}
}
The final King_OF_THE_HILL contract should look as shown below
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./interface/IDia_Oracle.sol";
contract KING_OF_THE_HILL{
IDIAOracle public idiaoracle;
constructor(address _IdiaOracleAddress){
idiaoracle = IDIAOracle(_IdiaOracleAddress);
}
//the king address
address public king_of_the_hill;
//claimThrone
function claimThrone(uint256 guessNumber)external{
require(guessNumber > 0,"guessNumber must be greater than 0");
require(guessNumber <= 100,"guessNumber must be less than or equal to 100");
uint256 generatedRandom = getRandomValue();
bool is_divible =isDivible(guessNumber, generatedRandom);
if (is_divible) {
king_of_the_hill = msg.sender;
}
}
//checkKingOfTheHill
function whosKing()external view returns(address){
return king_of_the_hill;
}
//getRandomNess from dia
function getRandomValue()private view returns(uint256 random){
uint256 latest_round = idiaoracle.getLastRound();
string memory random_ness_String = idiaoracle.getRandomValueFromRound(latest_round);
return stringToUintWithAbiEncode(random_ness_String);
}
//check if is divible by the number
function isDivible(uint256 _guess,uint256 _random_number)public pure returns(bool){
return _random_number % _guess == 0;
}
function stringToUintWithAbiEncode(string memory str)private pure returns(uint256){
return (uint256(keccak256(abi.encodePacked(str))));
}
}
Step 5: Deploying Script
Now the contract is ready to be deployed:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13 ;
import {Script,console} from "forge-std/Script.sol";
import {KING_OF_THE_HILL} from "../src/King_of_the_hill.sol";
contract DeployScript_King is Script{
function setUp()public{}
function run()external returns(KING_OF_THE_HILL){
uint256 my_privateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(my_privateKey);
//deploy the counter
address DiaRandomNess= 0xAd19b5b3A0BF2bAcf028ED6cc9E3Fc153cB405b6;
KING_OF_THE_HILL kingofthehill = new KING_OF_THE_HILL(DiaRandomNess);
console.log("Deployed to",address(kingofthehill));
vm.stopBroadcast();
return kingofthehill;
}
}
Where the DiaRandomNess is the address of the randomness contract of Dia deploy on crossfi
Step 6: Testing the contract
Since we are testing the contract on a local environment, we need to mock the DiaRandomess smart contract.
Create a new folder mock under the src and inside the mock folder create a Dia_oracle.sol file and include the code snippet below:
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;
}
}
Now create a test file under the Test folder and write test as shown below:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Test,console} from "forge-std/Test.sol";
import {DIARandomOracle} from "../../src/mock/Dia_Oracle.sol";
import {KING_OF_THE_HILL} from "../../src/King_of_the_hill.sol";
contract KING_OF_THE_HILL_TEST is Test{
KING_OF_THE_HILL public kingOfTheHill;
DIARandomOracle public diarandom;
address public one;
address public two;
address public three;
address public four;
address public five;
function setUp()public{
diarandom = new DIARandomOracle();
kingOfTheHill = new KING_OF_THE_HILL(address(diarandom));
one = vm.addr(1);
two = vm.addr(2);
three = vm.addr(3);
four = vm.addr(4);
five = vm.addr(5);
}
function test_contract_deployment()public view {
console.log("The address of the king of the night",address(kingOfTheHill));
assertNotEq(address(kingOfTheHill), address(0));
}
//function setRandomValue(uint256 _round, string memory _randomness, string memory _signature, string memory _previousSignature)
function test_setRandomValue(uint256 _round,string memory _randomness,string memory _signature,string memory _previousSignature)public{
if (_round == 0) {
_round = 1;
}
require(_round > 0, "Round must be greater than zero");
diarandom.setRandomValue(_round, _randomness, _signature, _previousSignature);
assertEq(diarandom.getLastRound(), _round);
}
function test_get_random_number_from_round()public{
uint256 _round = 1;
string memory _randomness = "1200";
string memory _signature = "0xabcdef";
string memory _previousSignature = "0x123456";
// Now you can call the function with these values
vm.startPrank(address(this));
test_setRandomValue(_round, _randomness, _signature, _previousSignature);
DIARandomOracle.Random memory rand = diarandom.getValue(_round);
assertEq(rand.randomness, _randomness);
vm.stopPrank();
}
function test_king_is_empty()public {
assertEq(kingOfTheHill.king_of_the_hill(), address(0));
}
function test_king_of_the_hill_claimed()public {
test_get_random_number_from_round();
vm.startPrank(one);
kingOfTheHill.claimThrone(4);
assertEq(kingOfTheHill.king_of_the_hill(), address(one));
vm.stopPrank();
}
}
Run the test:
forge test --match-contract KING_OF_THE_HILL_TEST
Step 7: Creating a make file for easy deployment
Under the Makefile file on the root of the directory create a deployment script":
deploy-king-of-the-hill:
forge script script/Deploy_King.s.sol:DeployScript_King --rpc-url crossfiRPC --broadcast --verify -vvvv
Run to deploy the contract to crossfi testnet:
make deploy-king-of-the-hill
With this implementation, we've successfully created a King of the Hill smart contract that integrates Dia Oracles for randomness. In the next article, we will explore further optimizations and potential enhancements.
Stay tuned
Subscribe to my newsletter
Read articles from Ronex directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ronex
Ronex
Blockchain/Backend developer ->->->I speak with code ->-> ->->->Gopher->->->