Quickstart
Randoo is pay-per-request on Ronin. Inherit RandooConsumerRonin (shipped with the sample below), quote the fee on-chain, and send it with the request.
- Deploy your consumer with the Randoo coordinator
0xC66eB2e7EE91145875000Ad46B55600000000001(same CREATE3 address on Ronin mainnet and Saigon). - Call
estimateRequestRandomFee(callbackGasLimit, gasPrice). - Call
requestRandomSeedwithmsg.value >= estimateand a non-zero refund address (not the consumer if it is not payable). - The oracle fulfills. Your
rawFulfillRandomSeedruns with the seed.
Consumer
TemplateConsumer is the canonical consumer sample. Its base RandooConsumerRonin ships with the sample and is inlined first; together with contracts/src/interfaces/IRoninVRFCoordinatorForConsumers.sol they compile as shown in a fresh Foundry project.
RandooConsumerRonin.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {IRoninVRFCoordinatorForConsumers} from "./interfaces/IRoninVRFCoordinatorForConsumers.sol";
/// @notice Ronin VRFConsumer-shaped base. Swap coordinator address to migrate.
abstract contract RandooConsumerRonin {
error OnlyCoordinatorCanFulfill();
address public vrfCoordinator;
constructor(address vrfCoordinator_) {
vrfCoordinator = vrfCoordinator_;
}
function rawFulfillRandomSeed(bytes32 reqHash, uint256 randomSeed) external {
if (msg.sender != vrfCoordinator) revert OnlyCoordinatorCanFulfill();
_fulfillRandomSeed(reqHash, randomSeed);
}
function _fulfillRandomSeed(bytes32 reqHash, uint256 randomSeed) internal virtual;
function _requestRandomness(
uint256 value,
uint256 callbackGasLimit,
uint256 gasPriceToFulfill,
address refundAddr
) internal returns (bytes32 reqHash) {
reqHash = IRoninVRFCoordinatorForConsumers(vrfCoordinator).requestRandomSeed{value: value}(
callbackGasLimit, gasPriceToFulfill, address(this), refundAddr
);
}
}TemplateConsumer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
// ---------------------------------------------------------------------------
// Randoo consumer template: copy-pasteable as a unit with
// RandooConsumerTemplate.t.sol and MockRandooCoordinator.sol (this folder), plus
// src/RandooConsumerRonin.sol and src/interfaces/IRoninVRFCoordinatorForConsumers.sol.
// Imports resolve in a fresh `forge init` with only forge-std installed.
// ---------------------------------------------------------------------------
import {RandooConsumerRonin} from "../../src/RandooConsumerRonin.sol";
/// @notice The canonical Randoo consumer. Copy it, rename it, put your game
/// logic in `_fulfillRandomSeed`.
///
/// Flow: a caller pays `estimateRequestRandomFee(callbackGasLimit, gasPrice)`
/// into `roll`, which forwards the request to the coordinator and returns the
/// `reqHash`. Later the coordinator calls `rawFulfillRandomSeed` (inherited,
/// coordinator-only) which lands in `_fulfillRandomSeed` with the seed.
contract TemplateConsumer is RandooConsumerRonin {
/// @notice Last seed delivered by the coordinator.
uint256 public lastSeed;
/// @notice Request hash of the most recent `roll`, then of the most recent fulfil.
bytes32 public lastReqHash;
constructor(address vrfCoordinator_) RandooConsumerRonin(vrfCoordinator_) {}
/// @param callbackGasLimit Gas the coordinator will give `_fulfillRandomSeed` (50k..2.5M).
/// @param gasPrice Gas price the fulfiller may spend; must be >= the coordinator floor.
/// @param refundAddr Receives unspent fulfilment gas; must be non-zero.
function roll(uint256 callbackGasLimit, uint256 gasPrice, address refundAddr)
external
payable
returns (bytes32 reqHash)
{
reqHash = _requestRandomness(msg.value, callbackGasLimit, gasPrice, refundAddr);
lastReqHash = reqHash;
}
/// @dev Your randomness lands here. Keep it within `callbackGasLimit`.
function _fulfillRandomSeed(bytes32 reqHash, uint256 randomSeed) internal override {
lastReqHash = reqHash;
lastSeed = randomSeed;
}
}Last verified 2026-08-25.