This repository is centered on contracts/WitnetRoulette.sol: a round-based roulette contract that consumes verifiable randomness from the Wit/Oracle Framework through Witnet's UsingWitRandomness integration.
The frontend remains in the repo, but the main purpose of this workspace is to demonstrate how an onchain game can actively depend on a Witnet randomness appliance for fair, auditable results.
WitnetRoulette does not generate randomness locally and does not trust block timestamps, block hashes, or operator-supplied values. Instead, it reads certified randomness from a deployed Witnet randomness appliance.
At a high level:
- Players place wagers for a future round.
- A Witnet randomness appliance is expected to have a successful randomization request on or after that round block.
- Once the appliance reports the round as randomized, the contract derives the roulette result from the Witnet-certified randomness.
- Players or the house claim winnings against that verifiable result.
This is the active-consumer model: your application logic actively depends on a live Witnet randomness appliance and reads randomness from it when business logic needs to settle outcomes.
The core contract is contracts/WitnetRoulette.sol.
It uses:
import "@witnet/solidity/contracts/UsingWitRandomness.sol";and inherits from UsingWitRandomness, which gives it access to the configured randomness appliance through the internal __witRandomness reference and helper methods such as _fetchRandomnessAfter(...).
The constructor receives an IWitRandomness appliance address:
constructor(
IWitRandomness randomizer,
uint16 roundIntervalBlocks,
uint16 ownerClaimDelayRounds
)That appliance is validated by UsingWitRandomness and becomes the contract's source of truth for randomness.
Roulette rounds are aligned to multiples of ROUND_INTERVAL_BLOCKS.
nextRoundNumber()computes the next eligible round block.wagerOnNumber(...)always stores bets for that future round.
This means bets are committed before the round they belong to is reached.
The contract checks:
__witRandomness.isRandomized(roundNumber)This gate is used before returning or settling results. If the appliance has not finalized a randomness value for that round yet, the contract will not settle it.
The actual roulette result is obtained through:
_fetchRoundResult(roundNumber)which internally uses:
_castADice(
MAX_NUMBER + 1,
roundNumber,
_fetchRandomnessAfter(roundNumber)
)This gives a result in [0, 36].
1..36are player-winning numbers.0is the house-winning number.
fetchRoundResult(roundNumber)exposes the round result as a view function.winningsOf(player, roundNumber, number)computes claimable winnings for a specific outcome.claimWinnings(roundNumber, number)transfers funds if the supplied number matches the Witnet-derived result.
Because the outcome is derived from the Witnet appliance, anyone reading the same randomized round gets the same verifiable result.
In this repo, WitnetRoulette is an active consumer in the sense that it is wired to a Witnet randomness appliance and actively reads certified randomness as part of its settlement logic.
Important nuance:
- The contract consumes randomness directly.
- The contract does not itself post
randomize()requests to the appliance. - An operator, bot, backend, or separate workflow must ensure the configured Witnet appliance is being fed with randomness requests for the rounds your app expects to settle.
So the operational model is:
- Keep the Witnet randomness appliance live.
- Ensure randomization requests are posted for the relevant blocks.
- Let
WitnetRouletteread and consume the certified result when claims happen.
If you want rounds to be continuously randomized, run a backend bot that sends randomize() requests to the same Witnet randomness appliance used by your deployed roulette contract.
The bot needs:
- an ETH/RPC signing gateway (from
ethrpc-gateway) with funds and a signer key - the target Witnet randomizer address (the same address passed as the
randomizerconstructor argument) - a schedule and network configuration
Core environment variables (see .env_witnet for the full template):
WITNET_SOLIDITY_RANDOMIZER_TARGET(required)WITNET_SOLIDITY_RANDOMIZER_NETWORK(for example,gnosis:testnet)WITNET_SOLIDITY_RANDOMIZER_GATEWAY_HOST(for example,http://127.0.0.1)WITNET_SOLIDITY_RANDOMIZER_GATEWAY_PORT(for example,8545)WITNET_SOLIDITY_RANDOMIZER_SCHEDULE(cron expression)WITNET_SOLIDITY_RANDOMIZER_SCHEDULE_TIMEZONE(for example,UTC)
- Start a signing gateway (example: Gnosis Chiado):
$env:ETHRPC_PROVIDER_URL="https://rpc.chiadochain.net"
$env:ETHRPC_PRIVATE_KEYS='["0xyourPrivateKey"]'
npx.cmd ethrpc-ethers $env:ETHRPC_PROVIDER_URL 8545- In a second terminal, run the randomizer bot:
$env:WITNET_SOLIDITY_RANDOMIZER_NETWORK="gnosis:testnet"
$env:WITNET_SOLIDITY_RANDOMIZER_TARGET="0xYourWitnetRandomnessAppliance"
$env:WITNET_SOLIDITY_RANDOMIZER_GATEWAY_HOST="http://127.0.0.1"
$env:WITNET_SOLIDITY_RANDOMIZER_GATEWAY_PORT="8545"
$env:WITNET_SOLIDITY_RANDOMIZER_SCHEDULE="*/5 * * * *"
$env:WITNET_SOLIDITY_RANDOMIZER_SCHEDULE_TIMEZONE="UTC"
npx.cmd --yes --package @witnet/solidity randomizer- Create an env file for the container, for example
.env.randomizer:
WITNET_SOLIDITY_RANDOMIZER_NETWORK=gnosis:testnet
WITNET_SOLIDITY_RANDOMIZER_TARGET=0xYourWitnetRandomnessAppliance
WITNET_SOLIDITY_RANDOMIZER_GATEWAY_HOST=http://host.docker.internal
WITNET_SOLIDITY_RANDOMIZER_GATEWAY_PORT=8545
WITNET_SOLIDITY_RANDOMIZER_SCHEDULE=*/5 * * * *
WITNET_SOLIDITY_RANDOMIZER_SCHEDULE_TIMEZONE=UTC- Run the container:
docker run --name witnet-randomizer --restart unless-stopped --env-file .env.randomizer witnet/evm-randomizer:3.1.0Notes:
- If your gateway runs on the host machine, use
host.docker.internalas gateway host from inside the container. - Make sure the signing account behind the gateway has enough native gas token to keep posting
randomize()transactions. - Keep schedule cadence aligned with your game activity and gas budget.
The contract itself relies on Witnet's interface guarantees through UsingWitRandomness. If you want to inspect or verify randomness offchain or from scripts, the configured appliance exposes methods such as:
isRandomized(blockNumber)fetchRandomnessAfter(blockNumber)fetchRandomnessAfterProof(blockNumber)verifyRandomnessAfter(blockNumber, witnetRandomness)
That is the core trust model: WitnetRoulette reads results from the appliance, and the appliance provides the verifiable path back to the underlying Wit/Oracle randomness source.
WitnetRoulette is deployed with three parameters:
randomizerThe deployed Witnet randomness appliance address.roundIntervalBlocksThe spacing, in blocks, between valid roulette rounds.ownerClaimDelayRoundsThe delay applied before the house can claim winnings from a round that resolved to0.
Current constructor rules:
roundIntervalBlocks > 0ownerClaimDelayRounds > 0
In the deployed contract, the delay is stored as:
ROUND_INTERVAL_BLOCKSHOUSE_CLAIM_DELAY_ROUNDS
The deploy script is scripts/deploy.ts.
It expects:
ROUND_INTERVAL_BLOCKS(default:10)HOUSE_CLAIM_DELAY_ROUNDS(default:1)
If either value is not set, the deploy script prompts for it interactively.
- Press Enter on
ROUND_INTERVAL_BLOCKSto use10. - Press Enter on
HOUSE_CLAIM_DELAY_ROUNDSto use1.
Before deploying, the script reads addresses.json and checks the WitnetRoulette entry for the target network.
- If a non-zero address already exists, it asks whether to redeploy.
- If you answer no, deployment is canceled and
addresses.jsonis unchanged. - If you answer yes, deployment continues and
addresses.jsonis updated with the new address.
RANDOMIZER_ADDRESS is only required on networks other than gnosis:testnet and gnosis:mainnet. On those two networks, the deploy and verify scripts resolve the Witnet randomness appliance address from the installed @witnet/solidity package.
Example:
$env:RANDOMIZER_ADDRESS="0xYourWitnetRandomnessAppliance"
$env:ROUND_INTERVAL_BLOCKS="10"
$env:HOUSE_CLAIM_DELAY_ROUNDS="1"
npm.cmd run hh:deploy:localGnosis examples:
$env:DEPLOYER_PRIVATE_KEY="0xyourPrivateKey"
$env:GNOSIS_TESTNET_RPC_URL="https://rpc.chiadochain.net"
$env:ROUND_INTERVAL_BLOCKS="10"
$env:HOUSE_CLAIM_DELAY_ROUNDS="1"
npm.cmd run hh:deploy:gnosis:testnet$env:DEPLOYER_PRIVATE_KEY="0xyourPrivateKey"
$env:GNOSIS_MAINNET_RPC_URL="https://rpc.gnosischain.com"
$env:ROUND_INTERVAL_BLOCKS="10"
$env:HOUSE_CLAIM_DELAY_ROUNDS="1"
npm.cmd run hh:deploy:gnosis:mainnetTo verify a deployed contract, set CONTRACT_ADDRESS and run the verify script on the same network. The script defaults to Blockscout on Gnosis networks.
$env:CONTRACT_ADDRESS="0xYourDeployedRoulette"
npm.cmd run hh:verify -- --network gnosis:testnetOptionally set VERIFY_PROVIDER to blockscout, etherscan, or sourcify.
- Compile contracts:
npm.cmd run hh:compile- Run tests:
npm.cmd run hh:test- Start a local Hardhat node:
npm.cmd run hh:node- Deploy the contract:
npm.cmd run hh:deploy:localThe frontend is still available if you want to build a UI around the roulette contract:
- Start dev server:
npm.cmd run dev- Build frontend:
npm.cmd run build- Preview build:
npm.cmd run previewThe current test suite exercises the contract around the active-consumer settlement model, including:
- deployment constraints
- immutable configuration values
- owner claim delay behavior
- owner vs non-owner claim restrictions
- pending liabilities interaction
- future-round claim rejection with explicit revert reason
Run the suite with:
npm.cmd run hh:test- contracts/WitnetRoulette.sol: main contract
- contracts/mocks/MockWitRandomness.sol: deterministic mock appliance for tests
- test/WitnetRoulette.ts: contract tests
- scripts/deploy.ts: deployment entry point
- contracts/README.md: shorter contracts-space note
If you want to actively consume verifiable randomness from the Wit/Oracle Framework in an onchain game, the pattern shown here is:
- Integrate
UsingWitRandomness. - Bind your contract to a valid Witnet randomness appliance at deployment.
- Express game state in terms of future blocks or rounds.
- Refuse settlement until the relevant round is certified as randomized.
- Derive outcomes exclusively from Witnet-certified randomness.
- Keep operational infrastructure in place so the randomness appliance is being actively served with requests.
That is the design center of this repository.