Appearance
🎲 Using Witnet Randomness
The Wit/Oracle Framework is a set of smart contracts deployed by the Witnet Foundation on a wide selection of EVM chains and L2s. This framework enables bidirectional interconnection between smart contracts and the Witnet blockchain.
The Witnet blockchain not only facilitates retrieving, aggregating, and notarizing real-world data on-chain but also serves as an unbiased and unpredictable source of randomness for smart contracts.
The Wit/Oracle Framework contains the WitRandomness utility contract, which basically allows anyone to request new randomness from Witnet by paying a small fee in EVM gas tokens (i.e. requesters), and anyone to freely fetch Witnet-generated randomness (i.e. consumers), even if requested by others.
Two randomness consumption models are supported:
| Active Consumers | Fetch previously generated random seeds on a WitRandomness address and within EVM transactions initiated by some EOA. |
| Passive Consumers | Receive a call once a previously requested random seed is generated and reported back from Witnet, within some rollup transaction initiated by the Wit/Oracle bridging infrastructure. |
To guarantee the impartiality and unpredictability of the random seeds provided by WitRandomness contracts, the following key aspects must be considered:
| Unpredictability | Active consumers must specify the EVM block number after which randomness must have been generated in Witnet, whereas passive consumers should gate the EVM block at which the provided random seed was actually requested. |
| Unbiasedness | On-chain randomization requires a two-step process. First, each randomization request is relayed to Witnet as an oracle query transaction. After several blocks, Witnet provides a new 256-bit random seed for each request. These results are sent back to the WitRandomness contract in the order they were made. |
TIP
Why is it different to other oracle solutions?
The random seeds generated by WitRandomness contracts within the Wit/Oracle Framework are trackable and verifiable. Each seed has an on-chain trail that allows users to trace the randomness generation process in Witnet, ensuring the transparency and trustworthiness of any on-chain outcomes derived from these seeds.
Actively reading randomized seeds
Actively reading Witnet-certified randomness is pretty cheap and simple:
solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.25 <0.9.0;
import {
UsingWitRandomness,
IWitRandomness
} from "@witnet/solidity/contracts/UsingWitRandomness.sol";
contract MyActiveRngConsumer1
is
UsingWitRandomness
{
constructor (IWitRandomness _witRandomness)
UsingWitRandomness(_witRandomness)
{
// Verify the contract is actually bound to the legit Wit/Oracle Framework:
require(
__witRandomness.witOracle() == address(0x77703aE126B971c9946d562F41Dd47071dA00777),
"invalid wit/oracle"
);
}
/// @notice Returns the 256-bit random seed as provided by the `randomizer`
/// contract, with the certainty that was securely generated in Witnet,
/// and not before the specified block number.
/// @dev Reverts if no secure random seed is still available for the specified block.
function fetchRandomizedSeed(uint256 _evmBlockNumber)
public view
blockIsRandomized(_evmBlockNumber)
returns (bytes32)
{
return _fetchRandomnessAfter(_evmBlockNumber);
}
/// ...
}Passively processing randomized seeds
Passive consumers only need to confirm that the reported seeds were actually requested on or after a specific reference block number:
solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.25 <0.9.0;
import {
Witnet,
UsingWitRandomness,
IWitRandomness,
IWitRandomnessConsumer
} from "@witnet/solidity/contracts/UsingWitRandomness.sol";
contract MyPassiveRngConsumer1
is
UsingWitRandomness,
IWitRandomnessConsumer
{
event Randomized(uint256 indexed expectedBlock, uint256 finalityBlock);
uint8 public constant DRAW_BALLS = 75;
uint8 public constant DRAW_EXTRACTIONS = 25;
/// @dev Schedule earliest block when next randomize callback will be accepted.
uint256 public nextRandomizeBlock;
struct Draw {
bytes drawnBalls;
uint64 expectedBlock;
uint64 finalityBlock;
uint64 witnetTimestamp;
bytes32 witnetTxHash;
}
Draw lastDrawInfo;
constructor (uint24 _maxCallbackGasLimit)
UsingWitRandomness(
// 1. Clone the legit WitRandomness contract in the Wit/Oracle Framework:
IWitRandomness(0xC0FFEE6912244068F3151F55AeF20fDe504B6E3a).clone(address(this))
)
{
// 2. Settle this contract as the one and only passive consuming target:
__witRandomness.settleConsumer(address(this), _maxCallbackGasLimit);
// 3. Transfer ownership of the just-cloned randomizing contract:
__witRandomness.transferOwnership(msg.sender);
// 4. Schedule the block when next randomness is expected:
nextRandomizeBlock = block.number;
}
function lastDrawnNumbers() external view returns (uint8[] memory _balls) {
bytes memory _lastDrawnBalls = lastDrawInfo.drawnBalls;
_balls = new uint8[](_lastDrawnBalls.length);
for (uint8 _ix; _ix < _balls.length; ++ _ix) {
_balls[_ix] = uint8(_lastDrawnBalls[_ix]);
}
}
/// ...
/// =======================================================================
/// --- IWitRandomnessConsumer methods ------------------------------------
/// @notice Process the random seed that's just being reported from Witnet.
/// @dev Reverts if called from an address other than `witRandomness`, or if
/// the provided `_randomness` was actually requested before the block
/// set in `nextRandomizeBlock`.
function reportRandomness(
bytes32 _randomness,
uint256 _evmRandomizeBlock,
uint256 _evmFinalityBlock,
Witnet.Timestamp _witnetTimestamp,
Witnet.TransactionHash _witnetTxHash
)
virtual override external
onlyFromWitnet
{
uint256 _nextRandomizeBlock = nextRandomizeBlock;
require(_evmRandomizeBlock >= _nextRandomizeBlock, "too hasty");
emit Randomized(_nextRandomizeBlock, _evmRandomizeBlock);
// Example: generate and store uniform extraction with no repetitions:
lastDrawInfo = Draw({
drawnBalls: _generateExtractionNoReps(
uint8(DRAW_BALLS),
uint8(DRAW_EXTRACTIONS),
_randomness
),
expectedBlock: uint64(_nextRandomizeBlock),
finalityBlock: uint64(_evmFinalityBlock),
witnetTimestamp: Witnet.Timestamp.unwrap(_witnetTimestamp),
witnetTxHash: Witnet.TransactionHash.unwrap(_witnetTxHash)
});
// Example: no more random seeds accepted before the next 256 blocks:
nextRandomizeBlock = block.number + 256;
}
function witRandomness() virtual override external view returns (IWitRandomness) {
return IWitRandomness(address(__witRandomness));
}
}Requesting on-chain randomness
To request on-chain randomness, you must pay a small fee in EVM gas tokens. This fee only depends on the gas price of the EVM transaction invoking the IWitRandomness.randomize() method:
solidity
// MyActiveRngConsumer1.sol | MyPassiveRngConsumer1.sol
/// ...
function __requestRandomize() override internal returns (uint256 _randomizeFee) {
_randomizeFee = __witRandomness.estimateRandomizeFee(tx.gasprice);
require(
msg.value >= _randomizeFee,
"insufficient fee"
);
_randomizeFee = __witRandomness.randomize{value: _randomizeFee}();
}