Quickstart

A consumer contract needs three things: forward the fee with a request, inherit the consumer base, and implement one callback. Complete working example:

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {IDicedEntropy} from "diced/IDicedEntropy.sol";
import {IDicedConsumer} from "diced/IDicedConsumer.sol";

contract CoinFlip is IDicedConsumer {
    IDicedEntropy public immutable entropy;

    event FlipResult(uint64 indexed seq, bool heads);

    constructor(address entropy_) { entropy = IDicedEntropy(entropy_); }

    function flip(bytes32 userRandom) external payable returns (uint64 seq) {
        // send at least getFeeV2(); any surplus is credited back to you
        seq = entropy.request{value: msg.value}(userRandom, 0);
    }

    function getEntropy() internal view override returns (address) {
        return address(entropy);
    }

    function entropyCallback(uint64 seq, address, bytes32 randomNumber) internal override {
        emit FlipResult(seq, uint256(randomNumber) & 1 == 0);
    }
}

Map the 32-byte word onto your outcome space with modulo arithmetic (uint256(randomNumber) % sides). One word is enough for many sub-outcomes โ€” slice bits or re-hash with a domain separator.

Generate userRandom client-side with crypto.getRandomValues per request. It's your half of the entropy: the provider committed its chain before your request, and you commit your contribution before the reveal.

Addresses

WhatValue
Oracle โ€” mainnet0x09aB0B2c0fAC9B3a8F28DEA45C71030F3c8A3B32
DicedEntropyV2 ยท source-verified ยท chain 4663
Oracle โ€” testnet0x9781059578EA4578952839a1ae6e6Cefe963D796
same source, own keeper ยท chain 46630 ยท faucet
Agent endpointPOST https://x402.diced.fun/x402/v1/random
HTTP 402, $0.05 USDG per word
RPChttps://rpc.mainnet.chain.robinhood.com
testnet: https://rpc.testnet.chain.robinhood.com
Explorerrobinhoodchain.blockscout.com
testnet: explorer.testnet.chain.robinhood.com
Legacy oracle (V1)0xc934f1c9e2A9CDdc19Ed2E4a1F88DC8d24EA48eE
still served, for consumers that hardcoded it โ€” don't build on it

API reference

Requesting

FunctionNotes
request(bytes32 userRandomNumber, uint32 callbackGasLimit) payable โ†’ uint64 V1-era entry point, kept for compatibility. On V2 prefer requestV2: it takes at least the fee and credits any surplus back to you, so a fee change cannot strand a request in flight. Gas limit 0 = provider default (200k), capped by the on-chain max (2M). Returns your sequence number.
requestV2() payable โ†’ uint64
requestV2(uint32) payable โ†’ uint64
Convenience overloads with an auto-derived user contribution (a salt โ€” fine when you just need "unpredictable to everyone but the provider", weaker than supplying your own).
requestWithToken(address token, bytes32 userRandomNumber, uint32 callbackGasLimit) โ†’ uint64 Pay the fee in an enabled ERC-20 (WETH at launch). Approve first; exact amount pulled, fee-on-transfer tokens rejected.

Reading

FunctionNotes
getFee() โ†’ uint128 / getFee(address token) โ†’ uint96Current fee. Always read at call time โ€” don't hardcode.
failedCallbackRandomness(uint64) โ†’ bytes32Your word, if your callback reverted (see below). Zero otherwise.
requests(uint64)Pending request struct; zeroed once revealed or refunded.

Refunds

refundRequest(uint64 seq) โ€” callable by anyone once a request has been pending longer than the refund delay (10 minutes at launch). The fee returns to the original requester in the original token, always. Native-fee consumers must be able to receive ETH (have a receive()), or use WETH.

Events

event Requested(uint64 indexed sequenceNumber, address indexed requester,
                address feeToken, uint96 feePaid, uint32 callbackGasLimit, bytes32 userContribution);
event Revealed(uint64 indexed sequenceNumber, address indexed requester,
               bytes32 randomNumber, bool callbackSucceeded);
event CallbackFailed(uint64 indexed sequenceNumber, address indexed requester,
                     bytes32 randomNumber, bytes reason);
event Refunded(uint64 indexed sequenceNumber, address indexed requester,
               address feeToken, uint96 amount);

The callback

Fees & refunds lifecycle

  1. Request: fee escrows inside the oracle, tied to your request.
  2. Reveal: escrow becomes operator revenue โ€” only now.
  3. No reveal: after the delay, anyone triggers your refund; escrow returns to you in full.

The operator cannot withdraw escrow of pending requests โ€” the accounting makes it impossible, not just impolite.

TypeScript

import { getFee, requestRandomness, waitForCallback } from '@diced/sdk';

const fee = await getFee(publicClient, ENTROPY);
const { sequenceNumber } = await requestRandomness(publicClient, walletClient, ENTROPY);
const { randomNumber } = await waitForCallback(publicClient, ENTROPY, sequenceNumber);

The SDK ships viem-based helpers plus the Solidity interfaces. Contact us for access while the repository is private.

Migrating from other entropy oracles

The consumer surface is deliberately Pyth-Entropy-shaped: requestV2(), the _entropyCallback(uint64, address, bytes32) selector, fee-forwarding semantics. If your contract already consumes a Pyth-style entropy service, migration is: point it at the diced.fun oracle address. The only behavioral differences to review are the flat (not gas-scaled) fee, the exact-msg.value rule, and the refund flow above.

Trust model

What is cryptographically guaranteed, and what you still take on trust. No hand-waving.

Guaranteed

Taken on trust

Pending โ‰  secret. Once any later sequence number is revealed, earlier chain elements are publicly derivable. Treat an undelivered outcome as unresolved, not as hidden information.