Install
The SDK ships the TypeScript helpers, the Solidity interfaces and a complete
worked example. viem is a peer dependency — nothing is bundled.
npm install @diced/sdk viem
For Solidity, copy solidity/ into your project or add a remapping:
@diced/=lib/diced-sdk/solidity/
Solidity interfaces
Two interfaces, depending on where you're coming from.
IDicedEntropy is the native surface. IEntropyV2 is
byte-compatible with Pyth Entropy V2 — if you already have a Pyth consumer, keep
your code and change one address.
Consumers inherit IDicedConsumer, which guards the
_entropyCallback(uint64,address,bytes32) selector so only the oracle
can deliver a result. That is the same selector Pyth's
IEntropyConsumer uses, so the two are interchangeable.
// 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(); surplus is credited back, gas limit 0 = default
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);
}
}
This exact contract is compiled and round-tripped against the oracle in the test suite, so it is known to work rather than merely illustrative.
Three rules that matter
- Quote the fee at call time.
getFeeV2()is a per-provider dial. Never hardcode it. - Keep the callback cheap. It runs under the gas limit you
requested. If it reverts, the reveal still happened — your word is retrievable
from
failedCallbackRandomness(provider, seq). - Supply your own
userRandom. The overloads without it derive a contribution in-contract, which weakens the model to "provider and sequencer don't collude".
TypeScript SDK
Every helper auto-detects which oracle generation it is talking to — only V2
answers getDefaultProvider, so one read distinguishes them. Pass
version explicitly to skip the probe in hot paths.
import { requestRandomness, waitForCallback, getFee } from '@diced/sdk';
const ORACLE = '0x09aB0B2c0fAC9B3a8F28DEA45C71030F3c8A3B32';
const fee = await getFee(publicClient, ORACLE);
const { sequenceNumber, provider } = await requestRandomness(
publicClient, walletClient, ORACLE,
);
const { randomNumber, callbackSucceeded } = await waitForCallback(
publicClient, ORACLE, sequenceNumber, { provider },
);
| Export | Purpose |
|---|---|
getFee(client, oracle, opts?) | Current fee in wei. opts.token prices an ERC-20; opts.provider selects a provider. |
requestRandomness(pub, wallet, oracle, opts?) | Files a request. Returns { sequenceNumber, txHash, userContribution, provider }. |
waitForCallback(client, oracle, seq, opts?) | Resolves on reveal. Watches events and backfills once, so a reveal landing before the watcher attaches is never missed. |
randomUserContribution() | 32 cryptographically random bytes for your contribution. |
detectVersion(client, oracle) | Returns 1 or 2. |
abiFor(v), dicedEntropyAbi, dicedEntropyV2Abi | Raw ABIs, if you'd rather drive viem yourself. |
provider to
waitForCallback whenever you have it.
Agent integration
If you can't reasonably hold ETH on an L3 and deploy a consumer contract — agents, scripts, backends — buy a word over HTTP instead. No wallet setup, no gas, no Solidity.
POST https://x402.diced.fun/x402/v1/random
An unpaid request returns HTTP 402 with the payment terms. Pay by
signing an EIP-2612 permit for $0.05 USDG and retrying with an
X-PAYMENT header. A successful reply looks like this:
{
"randomNumber": "0x339d160f5ea5219eaf4b4fd56f6e2a52ece339b1976a65e3b81b5746491ced5a",
"proof": {
"oracle": "0x09aB0B2c0fAC9B3a8F28DEA45C71030F3c8A3B32",
"sequenceNumber": "4",
"requestTx": "0xbb4cf7ea…",
"revealTx": "0x5000d278…",
"revealBlock": "26224269"
},
"payment": { "settlementTx": "0x9bda1a51…", "amount": "50000" }
}
Choosing a provider
V2 is multi-provider. Each provider commits its own hash chain, sets its own fee and keeps its own fees; registration is permissionless. Omit the provider argument and you get the default one.
This matters for liveness. A provider cannot change your outcome, but it can
withhold one. If that happens your fee is refundable permissionlessly after the
refund delay — and you can simply request from a different provider. Read
getProviderInfoV2(address) for a provider's fee, chain position and
remaining capacity before you commit to it.
Quick reference
| Oracle — mainnet | 0x09aB0B2c0fAC9B3a8F28DEA45C71030F3c8A3B32 |
| Oracle — testnet | 0x9781059578EA4578952839a1ae6e6Cefe963D796 |
| Chain | Robinhood Chain 4663 · testnet 46630 |
| Fee | 0.00002 ETH per request (per provider) |
| Typical latency | 1–3 seconds |
| Refund delay | 10 minutes, then permissionless |
| Callback gas | 200k default, 2M max |
| Agent endpoint | POST https://x402.diced.fun/x402/v1/random |
| License | Apache-2.0 |
Both oracles are source-verified — read the code you're trusting before you send it anything.