In August I wrote on this blog that oracle-side delay in a verifiable randomness request is “usually seconds, occasionally not”, and then did what everyone does with an inconvenient tail: left it as an adjective. That has bothered me ever since. So I went and measured it.
Between 18:29:09 UTC on 4 October and 18:29:07 UTC on 5 October 2026, I pulled every log the Chainlink VRF v2.5 coordinator emitted on Base mainnet. That is 43,200 blocks, exactly 86,398 seconds, and 1,893 randomness requests. All 1,893 came back. Nothing was left hanging.
The latency numbers turned out to be fine. The number that stopped me was five.
TL;DR
- 1,893 VRF requests on Base in 24 hours, every one fulfilled. Median wait: 3 blocks, 6 seconds. 99.05% were back inside 6 blocks. The slowest took 156 blocks, 5 minutes and 12 seconds.
- 1,691 of those requests (89.3%) asked for zero block confirmations. Not one request in the entire day was fulfilled in fewer than two blocks. The parameter you set is not the wait you get.
- Every fulfilment arrived from five externally owned addresses, routed through a single contract. Three of them rotate the 2 gwei lane; one address carried 397 of the 398 deliveries on the 30 gwei lane.
- Four callbacks reverted after the randomness was delivered and paid for. Three came from a contract called Dice. The explorer marks those transactions as successful.
- Median cost of a draw’s randomness: 0.000004 ETH, about one cent. The entire chain’s VRF bill for the day was roughly 18 dollars.
What I actually measured, and how you can redo it
The VRF v2.5 coordinator on Base lives at 0xd5D517aBE5cF79B7e95eC98dB0f0277788aFF634. Chainlink publishes that address, and Blockscout labels the deployed bytecode VRFCoordinatorV2_5_Optimism, which is the OP-stack variant that also bills you for L1 data.
I asked for every log that address emitted between blocks 52,174,601 and 52,217,800, in 1,000-block slices, through a public Base RPC gateway. Three event types came back, 1,893 of each: RandomWordsRequested, RandomWordsFulfilled and L1GasFee. Between them they carry the requested confirmations, the callback gas limit, the key hash, the payment, and a boolean called success. Pair them on request ID and you have the whole day.
Because I have written at length about not trusting a single stranger’s node, I re-pulled the final 500 blocks from mainnet.base.org, different software run by a different company. Seventy-eight logs from each, byte-identical. None of this is something you have to take my word for.
Base blocks are spaced at exactly two seconds, which is a counter rather than a clock and the reason every block gap below converts to seconds by doubling. Nothing here is estimated.
The third event is worth a sentence on its own. L1GasFee fires once per fulfilment because this is an OP-stack chain and the coordinator separately bills the consumer for the cost of posting that transaction’s data to Ethereum. It is why the price of an identical draw moved by a factor of nine across the day, from 0.00000136 ETH at the cheapest to 0.00001297 at the dearest. Your randomness is priced on a chain your game never touches.
Almost nobody asked to wait. Everybody waited.
Chainlink’s own Base configuration sets minimum confirmations to 0 and the maximum to 200. On Ethereum mainnet the same product will not let you go below three, because a reorg there can rewrite the block your request landed in and an attacker who sees the answer coming could try to unmake the question. Base gets a zero because an L2 with one sequencer has a different shape of risk, not because it has none, and you pay for the difference elsewhere: the premium on a Base request is 60% when you settle in ETH against 24% on mainnet.
Given the option, contracts take it. 1,691 of 1,893 requests specified zero confirmations. Another 129 asked for one, 72 asked for three, and exactly one asked for two.
Here is what they got.
| Fulfilled within | Requests | Share |
|---|---|---|
| 1 block (2 s) | 0 | 0.00% |
| 2 blocks (4 s) | 859 | 45.38% |
| 3 blocks (6 s) | 1,344 | 71.00% |
| 4 blocks (8 s) | 1,659 | 87.64% |
| 5 blocks (10 s) | 1,781 | 94.08% |
| 6 blocks (12 s) | 1,875 | 99.05% |
| 20 blocks (40 s) | 1,886 | 99.63% |
| 156 blocks (312 s) | 1,893 | 100.00% |
Zero requests came back in one block. Not a single one, out of 1,893, despite nine in ten of them asking for no delay whatsoever. That floor is not a policy, it is physics: the proof has to be generated off chain and then submitted, and the submission is itself a transaction that has to be built, broadcast and included. Setting confirmations to zero is permission to be fast. Four seconds is what fast costs.
That matters for anyone running a timed game. If your raffle closes entries when the request lands, the wait cannot change who was in the draw. If your contract treats the fulfilment as the closing bell instead, you have handed a window of between four seconds and five minutes to whoever is watching, and you did not choose its width. Nobody did.
What a day of requests actually looks like
Before the interesting part, the shape of the traffic, because it says something about who is using this.
Requests ran at roughly 79 an hour and never stopped. The busiest hour was 20:00 UTC with 102 and the quietest was 19:00 with 59, which is a flatter curve than any human activity produces. Randomness demand on Base does not sleep, because most of it is not a person pressing a button.
Base permits up to 500 random values per request and a callback gas limit of 2,500,000. Almost nobody goes near either ceiling. Two thirds of requests asked for three words, most of the rest asked for one, and the largest single request of the day wanted ten. On gas, 1,400 requests set the limit at exactly 500,000 and another 325 at 100,000, with just 25 requests pushing the maximum. These are round numbers chosen by developers rather than measurements, which suggests very few people have profiled the one function they cannot retry.
Five addresses delivered the lot
Now the part I did not expect to write. I resolved all 1,893 fulfilment transactions back to their senders, and here is the complete list of accounts that delivered verifiable randomness to Base mainnet that day:
| Address | Fulfilments | Share | Lane |
|---|---|---|---|
| 0x1cb7…05d4 | 511 | 27.0% | 2 gwei |
| 0xc60e…4873 | 492 | 26.0% | 2 gwei |
| 0x19a1…dd5a | 492 | 26.0% | 2 gwei |
| 0xedfe…9815 | 397 | 21.0% | 30 gwei |
| 0xd65f…1a84 | 1 | 0.1% | 30 gwei |
Every one of those 1,893 transactions went to the same destination, a verified contract named BatchVRFCoordinatorV2Plus. Four addresses did 99.9% of the work. The 2 gwei key hash was served by three of them in a near-perfect rotation. The 30 gwei key hash, the premium lane you pay extra for, was served by one address for 397 of its 398 deliveries.
Be precise about what that does and does not mean, because the loose version of this observation is wrong. It does not touch fairness. A VRF proof is verified on chain by the coordinator before your callback is ever invoked. You can watch it happen: inside those fulfilment transactions are static calls to precompiles 0x01 and 0x05, which is elliptic curve recovery and modular exponentiation, which is the proof check. A transmitter cannot submit a number other than the one their key commits to, and the chain will reject them if they try. The integrity guarantee is cryptographic and it holds regardless of how many addresses are involved.
What those addresses can do is not submit. Integrity is cryptography. Availability is operations, and on Base that day, operations had a headcount of about four.
It is worth sitting with how little recourse that leaves. On an L2 the usual comfort is forced inclusion: if the sequencer censors you, you can post your transaction through Ethereum and it has to be accepted. That escape hatch does not exist here, because the thing you need is not your transaction, it is a signature only one private key in the world can produce. Nobody can force-include a VRF proof on your behalf. If the keyholder for your lane goes quiet, your draw waits until it comes back, and the chain being perfectly healthy makes no difference at all.
That is not an argument against VRF. The alternative to depending on a keyholder is depending on a casino operator’s database, which is strictly worse in every direction, and the 1,893 for 1,893 completion rate says the dependency is being honoured. It is an argument against pretending the dependency is not there. Every game on this chain, ours included, has a liveness assumption with a handful of addresses in it, and almost none of them say so on the marketing page.
There is a hint in the data that this is not purely theoretical. The lane with three rotating deliverers had a 99th percentile of 6 blocks and a worst case of 50. The lane with one deliverer had a 99th percentile of 15 and a worst case of 156. One day is not proof of causation, but the slower lane is the lonelier one, and if you are paying the 30 gwei premium expecting a better tail, check that assumption yourself.
Four results that arrived, got paid for, and bounced
Four of the day’s 1,893 fulfilments came back with success = false. The coordinator verified the proof, called the consumer contract, the consumer reverted, and the subscription was charged anyway. That is 0.21%, which is small, and it is also the exact failure mode I described in theory back in August and could not then point at. Now I can point at four of them.
Three came from one contract, and the contract is named Dice. It made ten requests in total, all inside a 92-block window between 06:19 and 06:24 UTC, every one with an identical callback gas limit of 1,000,000. Seven succeeded, three reverted. Not gas starvation, then, because the same limit was comfortably enough seven times in the same five minutes. Something in that game’s own state rejected the delivery.
Here is the ugly bit. Pull one of those fulfilment transactions up on a block explorer and it reads status: ok, result: success. Green tick. The revert is one level down, in the internal call from the batch coordinator to the game contract, and you only see it if you open the internal transactions tab. A player checking whether their roll went through would see a successful transaction, a charged subscription, and a game that never moved.
The fourth failure was the slowest request of the entire day: 156 blocks of waiting, from an unverified contract, and then a revert at the end of it. Worst wait and a dead delivery in the same request.
What a draw actually costs
Thirty-three consumer contracts across 23 subscriptions used VRF on Base that day. Among the verified ones: a mining game, a game manager proxy, a contract called Coinflip and the aforementioned Dice. The median ETH-paid request cost 0.000004034 ETH, about 1.1 US cents at the spot price while I was writing this, and the chain’s entire randomness bill for 24 hours came to 0.00666 ETH, roughly 18 dollars.
Eighteen dollars for a full day of numbers nobody can rig. A centralised casino spends more than that on one minute of a compliance consultant, and it still will not show you its RNG.
What we take from it
The honest version of a system is the one that names its own limits, so here is what this changes about how we think about Satoshie, and what you should check on us.
The draw closes on the request, not the answer. Entry eligibility should be fixed by the block the randomness is requested in. Anything that depends on when the answer arrives is depending on a window that four strangers’ infrastructure decides.
Design for the tail, not the median. Six seconds is typical and irrelevant. Five minutes and twelve seconds happened inside that one window, to somebody, and a game whose UI promises an instant result is writing a cheque the oracle never signed.
Keep the callback boring. Three reverts out of ten from one contract, with a million gas available, is a warning about doing clever things in the one function you cannot retry on demand. Pick the winner, record it, pay out separately. The callback should be the least interesting code in the system, and if a profiler has never been pointed at it, 500,000 is a guess rather than a limit.
Assume the delivery can fail and make the exit permissionless. A request marked fulfilled and charged for while the game sits stuck is not an edge case, it is 0.21% of an ordinary day. The only acceptable answer is a timeout that anyone can trigger, not a refund you have to ask us for. Worth asking of us as readily as of anyone else.
Provable fairness is a claim about a number. It has never been a claim about when the number shows up, who hands it over, or whether your contract was awake when it did. Those are separate questions with measurable answers, and now the answers for one ordinary day on Base are written down: 6 seconds, five addresses, and a 0.21% failure rate on deliveries that the explorer will tell you went fine.
Data: Chainlink VRF v2.5 coordinator on Base mainnet, blocks 52,174,601 to 52,217,800, 4 to 5 October 2026. Cross-checked against the official Base RPC endpoint.
📷 Photo by William Warby on Unsplash


