Verifiable Randomness (VRF)
Verifiable onchain randomness on MegaETH via DrandOracleQuicknet — a preinstalled, stateless BLS12-381 verifier for the public drand quicknet beacon.
MegaETH ships with a preinstalled verifiable random function (VRF) service: DrandOracleQuicknet, a stateless BLS12-381 signature verifier deployed at a fixed address on each MegaETH network. Any contract can consume it. The randomness itself comes from drand, a public randomness beacon independently produced by a global network of participants and freely downloadable over HTTP.
At the highest level:
Your app commits to a specific future drand round.
Anyone — user, relayer, keeper, bot — fetches that round's signature from the public
api.drand.shonce it's published.They submit it in a transaction;
DrandOracleQuicknet.verifyNormalized(round, sig)checks the BLS pairing onchain and returns a canonical 32-byte random value.
For a complete worked example — contract, tests, and an end-to-end shell demo — see the Drand VRF Lottery.
What is VRF?
A verifiable random function produces a random value together with a proof of its validity. Given the proof and a public verification key, anyone can independently check three properties:
Correctness. The value was produced according to the VRF's public rules — no one can forge it.
Uniqueness. For any given input, there is exactly one valid output. The producer cannot choose among alternatives to bias the result.
Unpredictability. Before the proof is published, the value is indistinguishable from random.
For onchain use, the property that matters most is public verifiability: the proof is small enough that a smart contract can check it directly, without trusting the producer.
The VRF service on MegaETH
DrandOracleQuicknet is pre-deployed at a known address on each MegaETH network. Treat it the way you'd treat ecrecover — a stateless verification function your contract can call without owning or operating anything.
Source and full ABI: Zodomo/DrandVerifier.
Addresses
Network
Chain ID
DrandOracleQuicknet
MegaETH Mainnet
4326
0x7a53a6eFA81c426838fcf4824E6e207923969b36
MegaETH Testnet
6343
0x4e1673dcAA38136b5032F27ef93423162aF977Cc
What it is (and isn't)
A regular Solidity contract at the address above — not an EVM precompile. You interact with it like any other contract via normal
CALL/STATICCALL.Completely stateless. Every call is a pure verification against hardcoded drand parameters (group public key, period, genesis, scheme). It has no owner, no upgrade path, no pause, and no storage that tracks anything per-caller.
Identical on every MegaETH network. Same source, same configuration (drand quicknet: 3-second period, G1 signatures). Only the deployed address changes per network.
Underlying protocol
The verifier speaks drand quicknet (bls-unchained-g1-rfc9380), a drand subnet that signs a new round every three seconds. Each beacon is a 48-byte compressed G1 BLS12-381 signature over sha256(uint64 round, big-endian) under a fixed threshold-BLS group public key. The verifier checks each signature with a pairing check through EIP-2537 BLS12-381 precompiles — which MegaETH supports natively.
For the protocol spec and security model, see the drand developer docs.
What is drand?
drand is a public randomness beacon produced by the "League of Entropy" — a coalition of independent organizations (Cloudflare, Protocol Labs, EPFL, universities, and more).
Every few seconds, drand participants each sign a predetermined message (derived from the round number) with their BLS key share. Once enough shares arrive, anyone can combine them into a single valid BLS signature under the group public key. That signature is the beacon. Hashing it yields a 32-byte random value.
This gives three properties that matter for onchain randomness:
Publicly verifiable. Anyone with the group public key can check any beacon.
Unpredictable. The output is unknown until enough honest participants sign — no single party can predict it.
Unbiasable. BLS signatures are deterministic; even a threshold majority cannot cherry-pick among outputs, only decide whether to produce one.
drand has multiple networks. MegaETH's DrandOracleQuicknet targets the quicknet scheme specifically — the 3-second unchained subnet with G1 signatures. Details on protocol variants are in the drand protocol specification.
How to use DrandOracleQuicknet
DrandOracleQuicknetSource, full ABI, and test vectors live at Zodomo/DrandVerifier — start there if you need anything beyond the summary below.
API surface
PERIOD_SECONDS() → uint64
Returns 3 — the drand quicknet period.
GENESIS_TIMESTAMP() → uint64
Returns 1692803367 — the Unix timestamp of round 1.
roundMessageHash(uint64 round) → bytes32
The 32-byte digest drand signs for a given round. Useful for offchain proof construction.
verify(uint64 round, bytes sig) → bool
Runs the pairing check. Reverts on malformed signature bytes.
safeVerify(uint64 round, bytes sig) → bool
Same, but returns false instead of reverting on malformed input.
verifyAPI(string json) → bool
Accepts a raw api.drand.sh JSON payload and verifies it. Convenient, costs extra gas for JSON parsing.
verifyNormalized(uint64 round, bytes sig) → (bool, bytes32, bytes32)
Verifies and, if valid, returns (true, normalizedRoundHash, chainScopedHash). Encoding-invariant — the correct choice when you derive randomness from the signature.
For randomness consumption, use verifyNormalized. It returns a canonical random value independent of whether the submitter handed you compressed or uncompressed signature bytes.
Minimal pattern
Under 50 lines, and the only external dependency is the address constant.
Fetching the beacon offchain
The drand API is public, unauthenticated, and served by multiple independent relays. Any of the paths below work — the JSON shape is identical.
The drand quicknet chain hash 52db9ba7…0c84e971 is fixed — don't change it.
Timing
drand quicknet publishes a new beacon every 3 seconds, deterministically. Round N becomes signable at GENESIS_TIMESTAMP + (N - 1) * 3 seconds (Unix), where GENESIS_TIMESTAMP = 1692803367. This cadence is fixed — there is no faster round under quicknet, so 3 s is the irreducible unit of VRF latency.
A single VRF cycle walks through four stages:
Commit tx included
t₀ — your dapp picks revealRound
one MegaETH mini-block (~10 ms)
Round produced
t₁ = GENESIS + (revealRound − 1)·3 s
1–2 drand periods (3–6 s from t₀)
Beacon live on API
t₂ ≈ t₁ + <1 s
threshold BLS aggregation latency
Reveal tx included
t₃ — submitter sends reveal(sig)
one MegaETH mini-block (~10 ms)
Minimum realistic VRF time
With revealRound = currentRound + 2 (the default in the Drand VRF Lottery), end-to-end is ~4–7 s from commit to settled randomness. The wide range comes from where in a 3-second round your commit tx lands: commit just before a round boundary and you wait nearly 3 s (one period); commit just after and you wait nearly 6 s (two periods).
Don't set revealRound = currentRound + 1. If your commit tx lands in the tail of the current round, drand may have already signed the next one by the time it's confirmed on MegaETH — defeating the "future round" property. revealRound ≥ currentRound + 2 gives you a full round of slack against timestamp races and mini-block reordering.
For apps that don't need low latency (weekly draws, cross-epoch reveals, cooldown periods) set revealRound further out to buy larger safety margins, at a direct 3-seconds-per-round cost.
Worked example
The Drand VRF Lottery is a complete Foundry project — src/DrandLottery.sol, test suite, deploy scripts, and an ./script/demo.sh that drives the full lifecycle end-to-end against a real MegaETH network. Clone it if you want something you can run immediately.
Security caveats
DrandOracleQuicknet answers exactly one question: "is this a valid drand beacon for this round?". Everything else — when to consume it, which round to use, how to lock application inputs — is your contract's responsibility. Get these three things right and you're safe; get any one wrong and the cryptography cannot save you.
The caveats below cover integration-level concerns — what your consuming contract must do to make drand randomness safe to use. For protocol-level concerns that sit below our layer — drand's threshold-honesty assumption, front-running by malicious drand nodes, DoS and liveness bounds, DKG assumptions — see drand's own Security Model. Your contract inherits those assumptions by consuming drand; they are not things DrandOracleQuicknet can enforce.
1. Commit to a future round and lock every outcome-relevant input at commit time
The round you consume must be one drand has not yet signed at commit time, and the entrant set, stakes, tier choices, or anything else that affects the outcome must all be pinned in the same transaction.
drand beacons are public. If you pick the current round, or leave any outcome-relevant input mutable after commit, the submitter can read the beacon offchain and only proceed when the result favors them. Three concrete rules your commit logic must enforce:
Derive
revealRoundfromblock.timestamp + safety margin. UsecurrentRound + MIN_FUTURE_ROUNDSwithMIN_FUTURE_ROUNDS ≥ 2on MegaETH (larger on slower chains — see Timing).Make failure loud with an explicit require. Inside
commit, after computingrevealRound, assert:Without this check, a stale or adversarial
block.timestamp(miner drift, reorg, arithmetic edge case) can silently produce arevealRoundthat drand has already signed — the tx succeeds, no revert, but an attacker watchingapi.drand.shhas already seen the outcome. Therequireturns a silent security break into a visible revert.Pin an exact round, not "≥ committedRound". Reject any reveal whose round argument doesn't match the stored
revealRoundexactly; otherwise the submitter gets to pick among several already-produced rounds.
And freeze application state — entrant set, stakes, tier choices, any outcome-relevant input — in the same commit transaction. An input that can still move after commit gives the submitter adaptivity even if the round itself is properly pinned.
2. Own the state the verifier doesn't
DrandOracleQuicknet is stateless by design. Replay prevention, freshness checks, and encoding canonicalization are all on your consuming contract.
Three things your contract must do that the verifier will not:
Freshness: require
block.timestamp >= publish_time(revealRound)inrevealso a premature submission cannot succeed by accident.Replay: flip a
settled/consumedstorage flag before the external verify call (checks-effects-interactions), so the same beacon cannot be consumed twice.Encoding: use
verifyNormalized. The same valid G1 point can be encoded as 48 bytes (compressed) or 96 bytes (uncompressed); if you hash raw signature bytes yourself, the submitter gets to choose between two different random values.verifyNormalizedhashes the canonical uncompressed point so both encodings produce the same output.
3. Handle drand stalls explicitly
drand can miss a round — network outages and DKG incidents happen. A contract that waits forever for a stalled round is a bricked contract.
Add an expiry or fallback path so the game can resolve if the beacon never arrives. For example, a cancel-and-refund function gated by block.timestamp >= publishTime + STALL_WINDOW, or retry against a later round. See the DoS scenarios in drand's security model for how long such stalls can plausibly last.
References
Drand VRF Lottery example — runnable consumer contract with deploy scripts and an end-to-end shell demo
Zodomo/DrandVerifier — source of
DrandOracleQuicknet, full test suite, gas snapshotdrand protocol specification — including beacon timing
Last updated