1. Introduction: EXORandomness (EXOR)
EXORandomness is a light random-number market contract and is fully decentralized governed, which means there is no way that an administrator or facility produces malicious data.
Instead of verification of cryptographic proof achieved in chainlink, everyone who integrates his contract with EXORandomness for requesting random numbers, needs to appoint a few specific addresses to post random numbers, in case of data published from unknown addresses.
2. Architecture
EXORandomness: You can think of it as a registry service, receiving any random request from Consumer
EXOConsumer: Any developer could deploy their own logic contract inheriting from this
Bot: Any developer could deploy their logic contract inheriting from this.Someone post a random number to a specific request id on EXORandomness, then gets rewards given by EXOConsumer

3. Get a Random Number
To consume randomness, your contract should inherit from EXORConsumerBase and define two required functions.
- requestRandomness, which triggers every single request to EXOR.
- fulfillRandomness, which is the function that receives and does something with verified random number. But if your fulfillRandomness function uses more than 200k gas, the transaction will fail.
Note1, the below values have to be configured correctly for Randomness requests to work.
- _EXORAddress - EXORandomness contract address on the corresponding network (OEC Mainnet, OEC testnet)
- _feeToken - erc20 token address, used for paying every single request to bot
- _datasource - appoint a necessary address to post random number
Note2, the contract should own enough fee token to pay the specified fee
Consumer example
demo.sol
pragma solidity ^0.6.10;
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* THIS IS AN EXAMPLE CONTRACT WHICH USES HARDCODED VALUES FOR CLARITY.
* PLEASE DO NOT USE THIS CODE IN PRODUCTION.
*/
contract demo is EXORConsumerBase, Ownable {
event RequestNonce(uint256 indexed nonce);
event DemoRandom(bytes32 indexed requestId, uint256 indexed randomness);
uint256 public timer;
/**
* Constructor inherits EXORConsumerBase
*
* Network: OEC Testnet
* _EXORAddress: 0xed9Cc20D01C4c5bA4db06791b2A6db3A1Adf4a03
* _feeToken: 0xc474786670dda7763ec2733df674dd3fa1ddc819 (an erc20 token address)
* _datasource: 0x898527f28d6abe526308a6d18157ed1249c5bf1e
*/
constructor(address _EXORAddress, address _feeToken, address _datasource) EXORConsumerBase(_EXORAddress, _feeToken, _datasource) public {
}
/**
* Callback function used by EXORandomness
* !!!!! You can turn a single result into multiple random numbers with your strategy
*/
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
emit DemoRandom(requestId, randomness);
}
/**
* Requests randomness to EXORandomness
*/
function requestRandomness() external {
requestRandomness(timer, 100);
timer = timer + 1;
emit RequestNonce(timer);
}
/**
* Enable or Disable a datasource
*/
function changeDataSource(address _datasource, bool _boolean) external onlyOwner {
datasources[_datasource] = _boolean;
emit DataSourceChanged(_datasource, _boolean);
}
}
EXORConsumerBase.sol
pragma solidity ^0.6.10;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
interface IEXOR {
function randomnessRequest(
uint256 _consumerSeed,
uint256 _feePaid,
address _feeToken
) external;
}
contract EXORRequestIDBase {
bytes32 private _keyHash;
function makeVRFInputSeed(
uint256 _userSeed,
address _requester,
uint256 _nonce
) internal pure returns ( uint256 ) {
return uint256(keccak256(abi.encode(_userSeed, _requester, _nonce)));
}
function makeRequestId(
uint256 _vRFInputSeed
) internal view returns (bytes32) {
return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed));
}
}
abstract contract EXORConsumerBase is EXORRequestIDBase {
using SafeMath for uint256;
using SafeERC20 for IERC20;
/* ================================================== STATE VARIABLES ================================================== */
// @notice requesting times of this consumer
uint256 private nonces;
// @notice reward address
address public feeToken;
// @notice EXORandomness address
address immutable private EXORAddress;
// @notice appointed data source map
mapping(address => bool) public datasources;
/* ================================================== CONSTRUCTOR ================================================== */
constructor(
address _EXORAddress,
address _feeToken,
address _datasource
) public {
EXORAddress = _EXORAddress;
feeToken = _feeToken;
datasources[_datasource] = true;
}
/* ================================================== MUTATIVE FUNCTIONS ================================================== */
// @notice developer needs to overwrites this function, and the total gas used is limited less than 200K
// it will be emitted when a bot put a random number to this consumer
function fulfillRandomness(
bytes32 requestId,
uint256 randomness
) internal virtual;
// @notice developer needs to call this function in his own logic contract, to ask for a random number with a unique request id
// @param _seed seed number generated from logic contract
// @param _fee reward number given for this single request
function requestRandomness(
uint256 _seed,
uint256 _fee
)
internal
returns (
bytes32 requestId
)
{
IERC20(feeToken).safeApprove(EXORAddress, 0);
IERC20(feeToken).safeApprove(EXORAddress, _fee);
IEXOR(EXORAddress).randomnessRequest(_seed, _fee, feeToken);
uint256 vRFSeed = makeVRFInputSeed(_seed, address(this), nonces);
nonces = nonces.add(1);
return makeRequestId(vRFSeed);
}
// @notice only EXORandomness contract can call this function
// @param requestId a specific request id
// @param randomness a random number
function rawFulfillRandomness(
bytes32 requestId,
uint256 randomness
) external {
require(msg.sender == EXORAddress, "Only EXORandomness can fulfill");
fulfillRandomness(requestId, randomness);
}
}
4. Bot
- If you want to be a bot to earn reward from consumer, you have to subscribe the event (below here) from EXORandomness.
// topic id: 0x1015600ee627b7f5e25ddb598b475e901bb156197761e8ee25c853c75d733c10
event RandomnessRequest(address indexed requester, bytes32 indexed requestId, uint256 indexed seed, address feeToken, uint256 feePaid)
And you have to make sure your address in this requester contract datasources map.
sign the message which is formed by requestId and random number in your private key, then call fulfillRandomnessRequest function to EXORandomness
Parameters
Name | type | comment |
message | bytes | requestId (uint256) + random number (uint256) |
signature | bytes | Ethereum-compatible ECDSA signatures |
5. Deploy Information