A Chainlink VRF subscription on Base is a wallet with a guest list. The balance pays for randomness; the guest list decides which contracts may spend it. Add a contract and it can draw. Remove it and it cannot. That list is the most consequential piece of configuration in on-chain gaming, because it is the complete set of code that can reach into a funded subscription and ask for a number.
Nobody has ever counted it. We have written about who owns these subscriptions, what they hold and how long they live. The guest list itself, the full history of every address ever added to or removed from a Base VRF subscription, has sat unread for 840 days. Tonight we pulled all of it: 2,279 additions and 482 removals across 1,361 subscriptions, naming 2,233 distinct addresses.
Sixty-four of those addresses have no code. Not one of them ever did.
TL;DR
- At Base block 52,995,512 the VRF coordinator’s complete consumer history is 2,279
SubscriptionConsumerAddedand 482SubscriptionConsumerRemovedevents over 2.30 years, covering 2,233 distinct addresses. - 544 of the 1,361 subscriptions ever created never wired a single consumer, so they could never have drawn even if funded. Only 817 ever got that far.
- 64 of the 2,233 authorised addresses hold no code, and
eth_getCodeat the exact block each was added proves none of them ever did. There are zero self-destructs here. 15 are plain keys and 49 are addresses that have never done anything on Base at all. - One of them is
0x5fbdb2315678afecb367f032d93f642f64180aa3, the contract address every Hardhat and Anvil tutorial produces on a local test node. It is authorised on two live Base subscriptions and holds 0.334136706 ETH. - 696 of the 817 subscriptions have never once run
removeConsumer. A single operator accounts for 19.5% of every removal in the contract’s history. - 344 of the 695 wired, still-active subscriptions (49.5%) have never requested randomness. Wiring a game contract is not the same as shipping a game.
The dataset, and the identity that proves it complete
The coordinator is 0xd5d517abe5cf79b7e95ec98db0f0277788aff634. Everything below is pinned at block 52,995,512, head minus twenty, timestamped 23 October 2026 at 18:32:51 UTC.
Five event types were pulled over the contract’s entire life: SubscriptionCreated (1,361), SubscriptionCanceled (185), SubscriptionConsumerAdded (2,279), SubscriptionConsumerRemoved (481 as served, 482 in reality) and SubscriptionOwnerTransferred (22).
Counts off an indexer are worth nothing on their own, so two independent checks run first. The coarse one: 1,361 created minus 185 cancelled is 1,176, which is exactly the length of getActiveSubscriptionIds(0,0), and that list is provably complete because asking for a start index equal to the count reverts rather than silently returning a short array.
The sharp one is new, and it is why any of these figures are publishable. Replaying the add and remove streams in block order reconstructs, per subscription, the exact consumer set that should be registered right now, which can then be compared against what the contract reports through getSubscription. We ran it against all 1,176 active subscriptions.
1,175 of 1,176 matched exactly, set for set. That validates 2,302 of the 2,760 events, 83.4% of the dataset, against live contract storage rather than against another copy of the same index. The one that did not match is a better story than the 1,175 that did, and it gets its own section below.
544 subscriptions were never wired to anything
Of the 1,361 ever created, 817 have at some point authorised at least one consumer and 544 never have. Among the 1,176 still active, 695 have been wired and 481 never have. That 481 is the hard floor under the 483 subscriptions we reported in August as holding no consumer contract: 481 never had one, and precisely two had one and gave it up.
The 481 that were never wired are not idle games. They are subscriptions where nobody got as far as deploying the thing that would use them. They hold 20.1611 LINK and 0.288734 ETH between them, and 424 hold nothing at all.
The sixty-four that could never answer
Running eth_getCode against all 2,233 distinct consumer addresses at the pinned block returns empty for 64, or 2.87%. That alone is ambiguous: an empty account today might be a contract that has since self-destructed, which would make this a story about cleanup rather than configuration.
It is not. We re-ran eth_getCode against each of the 64 at the exact block it was added. All 64 were already empty when they were authorised. Zero had code. There are no self-destructs here. Every one was wired to a live mainnet subscription while pointing at nothing.
Pairing that with eth_getTransactionCount splits them cleanly in two.
| What the address actually is | Count | How we know |
|---|---|---|
| A plain key, with transaction history | 15 | No code, nonce above zero (highest is 905) |
| An address that has never existed on Base | 49 | No code, nonce zero, 47 of 49 hold zero balance |
| Total authorised but codeless | 64 | Spread over 38 subscriptions, added between 26 Sep 2024 and 16 Oct 2026 |
The second row is the strange one. An address with nonce zero and no code has never sent a transaction and has never had code deployed to it. It has never been msg.sender of anything, which means those 49 provably never requested randomness. That is not a measurement, it is arithmetic: a nonce cannot go down.
These are addresses computed somewhere else and pasted in: a CREATE2 prediction never deployed, a deployment address copied from another chain, a figure read off the wrong line of a script. The coordinator does not care. addConsumer takes an address and stores it, with no check anywhere that it is a contract.
Forty-six of these codeless addresses are still on the guest list right now, across 28 active subscriptions. Nine of those 28 have drawn randomness. Twenty-four are carrying money: 3.357581 LINK and 0.682038 ETH sit on subscriptions whose authorised list includes an address that has never held a line of code.
That a codeless consumer cannot receive a callback is not a new observation. We found four of them in a 174-address sample, and one among the 59 contracts that actually draw. What is new is the full census: how many there are across every subscription ever created, that none was ever a contract, and that half of the live ones sit on funded subscriptions.
The address from the tutorial
One entry in that list of 49 is 0x5fbdb2315678afecb367f032d93f642f64180aa3. If you have ever run a Solidity deployment against a local node, you have seen it.
We derived it rather than recognised it, because recognising things is how published numbers go wrong. A contract address created by a plain deployment is the last twenty bytes of the Keccak hash of the RLP encoding of the sender and nonce. Hardhat’s and Foundry’s shared default account zero is 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266. RLP-encoding that address with nonce zero gives 0xd694f39fd6e51aad88f6f4ce6ab8827279cfffb9226680, whose Keccak hash ends in exactly those twenty bytes. It is the first contract anyone deploys on a fresh local chain, every time, deterministically.
On Base mainnet it has no code and a nonce of zero. It also has 0.334136706 ETH on it, sent by people who copied the same address out of the same tutorial. And it is wired as a live VRF consumer on two active subscriptions, one of which has made 53 draws and carries ten consumers in total.
Nothing is being exploited here. Nobody can make that address do anything, which is rather the point: a localhost artefact has been granted permission to spend from two real subscriptions on a real chain, and has sat there unnoticed because nothing in the system ever looks. This differs from our earlier work on the Hardhat test keys, which covered accounts with balances and signed delegations; this is the deterministic contract address those keys produce, holding an authorisation.
Removing a consumer is not a deployment step
When we looked at subscription owners in August we found an operator rotating its consumer contract on a twelve-hour schedule and wrote that removeConsumer “is not a break-glass function that sits untouched for years”. Against the full population, that sentence needs an amendment, and it is the most useful thing in this dataset.
696 of the 817 subscriptions that ever wired a consumer have never run removeConsumer once. Only 121, 14.8%, have ever removed anything. And 485 of the 817, 59.4%, added exactly one consumer and never touched the list again, which for 426 of them that are still active is the entire configuration history of the subscription.
Removal is also extraordinarily concentrated. Of the 482 removals in the contract’s history, 94 belong to a single subscription (19.5%) and the top three account for 32.2%. For almost everybody else the guest list is written once at deployment and never read again.
That inverts the risk. The worry about a mutable consumer list is that somebody swaps the game contract underneath you. The measured reality is that the list is almost never touched, so the real exposure is the opposite: whatever was authorised on day one is still authorised today, including the mistakes.
Two operators at the opposite extremes
The distribution of additions per subscription is brutally skewed. The median subscription that ever wired a consumer wired exactly one. The ninetieth percentile is six. The maximum is 97.
| The rotator | The hoarder | |
|---|---|---|
| Consumers added | 97 | 49 |
| Consumers removed | 94 | 0 |
| Authorised right now | 3 | 49 |
| Distinct bytecodes across them | 1 | 44 |
| Lifetime draws | 95 | 5,668 |
| Active since | 7 Sep 2026 | 16 Oct 2025 |
The rotator deploys a fresh contract, adds it, and removes the previous one about ten minutes earlier, twice a day: median gap between additions 12.00 hours, median consumer tenure 11.83 hours, unbroken since 7 September. The new fact, which a two-week window could not show, is that all 97 of its consumers share one bytecode hash and one code length of 5,382 bytes. It is not iterating on a game. It is redeploying the identical contract 97 times and drawing roughly once with each.
The hoarder is the inverse and the more interesting risk. It has added 49 consumers since October 2025 and removed none. Forty-four distinct bytecodes, so these are genuinely different contracts, and three are codeless addresses from the census above. All 49 remain authorised to spend from one balance, across 5,668 draws. The authorisation surface only ever grows.
Across the whole population, 230 of the 1,176 active subscriptions have more than one live consumer, and they hold 706.6547 LINK and 5.029105 ETH, which is 47.8% and 48.9% of all the value on the coordinator. Roughly half the money sitting in Base VRF is reachable by more than one contract.
Wiring happens at deployment, or it does not happen
The gap between a subscription’s creation and its first consumer gives the cleanest picture of how these are actually built. Across all 817 the median is 11.9 minutes.
The median hides the shape. 156 subscriptions wired their first consumer in the same block they were created. 230 did it within sixty seconds, 395 within ten minutes, 734 within a day; the quarter-point is twenty-four seconds. This is not a decision anybody deliberates. It is one script doing create-then-add before the operator has looked away.
Half of the wired never drew
Of the 695 active subscriptions that have authorised at least one consumer, 344 have a lifetime request count of zero. That is 49.5%. Somebody deployed a contract, deployed it to Base mainnet, authorised it against a funded or unfunded subscription, and then never asked for a single random number.
The arithmetic closes exactly, which is a useful check on both halves: 481 active subscriptions never wired anything and 344 wired something and never drew, and 481 plus 344 is 825, which is precisely the number of active subscriptions with a request count of zero. No subscription has ever drawn without a registered consumer.
The row the indexer dropped
The one subscription whose replay did not match live state is the rotator. Our reconstruction gave it four consumers; the contract reports three. The extra one, 0xe6ef08131a7111e48c25a77a8d1f547607176a34, was its first consumer back in September.
Rather than assume a bug in either direction, we binary-searched the archive node, reading getSubscription at historical blocks to find the exact block at which that address left the array. It is block 50,999,800. Pulling that single block’s logs from an archive RPC returns the missing event: a SubscriptionConsumerRemoved at log index 598 of transaction 0x722a8b445c93ff4eb3bfe6398c8175efb2dfff4e7971dc58ebdd2fe4279d3d71, timestamped 7 September 2026 at 13:49:07 UTC. The indexer’s full-history query returns it nowhere, at any page, with "message":"OK" throughout.
So the true removal count is 482, not the 481 served. One row in 2,761, about 99.96% complete, and silently so. We have caught this before using a balance identity. The generalisation is that a set identity works just as well and is available far more often: any event stream describing mutations to a collection can be replayed against that collection’s current state, and a disagreement localises the missing row without a second data source. The second source is only needed to name it.
Three posts this is not
Overlap was measured by regex-reading the five nearest neighbours before drafting. The owner census owns who can call these functions, and it found the rotator; this owns what those calls produced across 840 days. The consumer-configuration post owns what happens inside a game contract once authorised, across the 59 that draw, where this covers all 2,233 allowed to. The lifespan post parked this dataset as unexamined.
One finding here is a re-sell and we would rather say so: the seventy byte-identical ERC-1167 proxies pointing at 0xd9f48a7303550ee99a7aee8005447dde1a11f22f appear in the consumer set, and we published 68 of those 70 two days ago. The full history puts the cluster at exactly 70, each on its own subscription.
What this means if you are about to play
Everything above describes a trust surface no player can see. When you enter a draw, the contract you are using holds an authorisation granted at some past moment by an owner you cannot identify, against a list that in 85% of cases has never been revised since the day it was written. Nothing in the interface tells you whether the subscription behind it has one authorised contract or forty-nine, or whether any of them points at an address that has never had code.
None of this breaks the randomness. Chainlink VRF’s cryptographic guarantee is intact in every case measured here, and a verifiable random number remains a verifiable random number whoever asked for it. The gap is not in the randomness, it is in everything around it, and that gap is measurable, which is the whole argument.
This is the standard Satoshie is built to meet. Provable fairness is not a badge in a footer; it is the claim that every link between a player’s entry and the number that decides it can be checked by a stranger, on-chain, after the fact. A guest list written once eighteen months ago and never audited is one of those links. So is a consumer address pasted in from a local test node. We publish these measurements against the whole population, including the inconvenient parts, because the alternative is asking people to take our word for it.
Honest limits
- The set identity covers active subscriptions only. Cancelling wipes the consumer list, so the 349 additions and 109 removals belonging to the 185 cancelled subscriptions cannot be checked against live state. That is 16.6% of the dataset resting on the indexer alone, and the indexer drops about one row in 2,761.
- We could not attribute draws per consumer. The coordinator has served 2,188,233 requests and attributing all of them was out of scope, so “49.5% never drew” is measured per subscription. A per-consumer query was attempted and abandoned when its own negative control failed too.
- “No code now” is not “no code ever”. A
CREATE2address empty today can be deployed to tomorrow, and an authorisation granted now would still be valid. Three of the 49 are plausible candidates. - The clustering is bytecode-level, not source-level. 2,169 of the 2,233 have code and only a minority are verified; two addresses sharing a hash run the same code but may differ entirely in constructor arguments and storage.
- The “never existed” claim is specific to Base. Those 49 may be busy contracts on another chain; nonce zero and empty code proves only that nothing has happened at that address here.
Three questions worth asking
- If you run a game on Base VRF, how many addresses are currently on your subscription’s guest list, and when did you last look? For 696 operators the honest answer is “never”.
- Should
addConsumerreject an address with no code? It would have stopped all 64, and also legitimateCREATE2pre-authorisation. A real trade-off, not an obvious fix. - Why does the coordinator let a subscription accumulate consumers forever with no ceiling and no expiry? Forty-nine live authorisations against one balance is not a configuration anybody chose; it is one that nobody ever cleaned up.
Run it yourself
Call getActiveSubscriptionIds(0,0) on 0xd5d517abe5cf79b7e95ec98db0f0277788aff634 for all 1,176 identifiers, confirming completeness by checking that a start index equal to the count reverts. Pull SubscriptionConsumerAdded (topic0 0x1e980d04aa7648e205713e5e8ea3808672ac163d10936d36f91b2c88ac1575e1) and SubscriptionConsumerRemoved (0x32158c6058347c1601b2d12bc696ac6901d8a9a9aa3ba10c27ab0a983e8425a7) over full history, deduplicating on transaction hash plus log index. Replay them per subscription and compare each reconstructed set against getSubscription. Then run eth_getCode and eth_getTransactionCount against every distinct consumer, once at the pinned block and once at the block it was added.
Every figure in this post comes out of those calls. If one of them is wrong, it is checkable, and we would like to know.


