The Zero That Lies: Null Propagation in On-Chain Data Pipelines
Hook
At 03:14 UTC my monitoring script wrote one line to disk: vault.collateral_ratio = null. Not zero. Null. The distinction matters more than most people building in this industry understand. Zero is a measurement. Null is an admission that no measurement exists.
My script returned null for forty-one minutes across nine lending markets on three chains. Nothing was broken on-chain. Every block was produced on schedule. Every transaction left a scar on the blockchain, and every scar was verifiable. The failure happened somewhere else entirely — in the thin, unaudited membrane between the chain and the humans who make decisions based on it.
Six days earlier I watched the same class of failure produce a different outcome. A dashboard rendered a vault as fully collateralized because the price feed for its collateral asset timed out and the front-end defaulted the missing number to zero. Zero debt. Infinite safety. The liquidation logic downstream read the same null, applied the same default, and skipped the position. In a market where that asset had moved eleven percent in nine minutes, a silent timeout became a silent immunity. A single unhandled data state froze a position that the market had already decided.
That is the thing about bull markets. They do not eliminate technical debt. They finance it.
Context
Methodology and data sources first, because conclusions without provenance are opinions wearing lab coats. Everything below comes from three inputs: raw JSON-RPC responses pulled from four independent node providers against matched block heights, subgraph metadata sampled at sixty-second intervals, and my own Python harness reconciling both against a locally synced archive node. Where I could not verify a claim against a block hash, I have labeled it inference rather than finding.
The pipeline that produces the numbers most traders act on looks like this. A node executes a call against a contract. An indexer walks logs and writes them into a database. A subgraph maps those logs into an entity schema. An API serves the entity. A front-end renders it. A bot consumes it. At no point in that chain does anyone certify that an empty field means what the consumer believes it means.
I learned this the hard way. In 2017 I spent three weeks auditing a staking reward distribution algorithm against the academic papers its founders cited. The mathematics held in the happy path and inverted in the edge case, because the edge case had never been parameterized. I filed a rejection instead of an endorsement. It was the first time I understood that a system can be internally consistent and externally wrong, and that the gap between those two states is where every downstream consumer gets hurt. Every report I have written since opens with where the numbers came from and what they cannot tell you.
There are six ways a field goes empty, and they are not equivalent:
- True zero. The value is genuinely zero. Rare, and usually benign.
- Reverted call. The contract refused to answer. The RPC layer returns an error, which clients routinely coerce to zero or null.
- Schema drift. The subgraph renamed an entity and the consumer query silently returns an empty set. No exception. Just absence.
- Rate limiting. The provider throttled you. Returning null is cheaper than returning a 429.
- Upstream timeout. The indexer is lagging. Its head sits behind the chain head and the entity does not yet exist at that block.
- Deliberate omission. Someone chose not to publish. That is the one worth losing sleep over.
Absence of evidence is not evidence of absence. In a ledger, it is evidence of a broken pipe.
Core
The evidence chain starts with one question: when did the subgraph's indexed head diverge from the chain head?
I sampled indexed block height every sixty seconds for fourteen days across eleven production subgraphs covering lending, DEX, and bridging activity. Median lag was three blocks. The ninety-fifth percentile was nineteen blocks. The worst observed window was 214 blocks — roughly forty-three minutes on a twelve-second chain. During that 214-block window the subgraph reported zero new liquidations. My archive node recorded 118.
A lagging indexer is not a slow indexer. It is a confident liar.
Here is the mechanism that converts a lag into a loss. A liquidation bot built on indexed data does not ask whether a position is unhealthy. It asks whether the position is unhealthy according to data it can currently see. When the index is behind, the bot sees collateral that has not yet been marked down and debt that has not yet accrued. It does not compute a wrong answer. It computes a correct answer to a stale question. Those two outcomes are functionally identical and morally different, and only one of them is fixable with a better model.
I ran the same reconstruction against a bot-farm signature I first documented during DeFi Summer, when forty percent of deposits into a major lending market traced to clusters exploiting new-account bonuses rather than organic demand. The shape rhymes. In both cases the headline metric was accurate and the thing it claimed to measure did not exist.
Now add the oracle layer. Reference feeds do not push a new price on every tick. They update on a deviation threshold or a heartbeat, whichever fires first — commonly a half-percent band against an hourly heartbeat. Consider what that means operationally. A feed can be fresh by its own definition and stale by yours. If the underlying moves twelve percent in nine minutes, the feed will have updated; but between updates there are windows measured in tens of seconds where the published price is materially wrong while the feed's own health check reports green.
Oracle latency is not a defect in the oracle. It is an architectural property that every protocol pretends does not exist.
I have written this before and I will write it again. Solving decentralization by running a permissioned node set and calling the result a protocol is a governance decision dressed in technical clothing. That is not an argument against price feeds. It is an argument against treating a freshness flag as a risk model. The flag answers a question about the feed. It does not answer a question about the market.
The second layer is the rollup. I tracked proving and batch-posting costs across two zk-rollups for ninety days, correlating per-batch proving expense against L1 gas. The relationship is not linear. It behaves like a fixed cost with a variable subsidy. Proving cost per batch stayed roughly flat while net sequencer revenue collapsed by more than seventy percent when L1 gas cooled. Operators do not stop proving. They defer proving. They widen batching intervals and prioritize the batches carrying the most fee revenue.
What does that deliver to a user? A state that is authenticated but not yet proven. Their balance is correct on the sequencer and unverifiable on L1 for longer. For integrators, the word finalized stops being a cryptographic property and becomes a policy choice. The proving cost is not paid by the operator alone. It is paid in finality latency, and finality latency is a null in the schema.
The third layer is intent-based routing. Solvers quote, aggregators compare, the best quote wins. When a solver endpoint returns an error or an empty array, most aggregators do not halt. They drop that solver from the auction and route to the next best. The user sees a filled order. Nobody sees that the auction was thinner than advertised.
Moving MEV from the mempool to a solver API does not eliminate the extraction. It relocates it behind a private endpoint where the order flow cannot be audited by the people paying for it.
I mapped wallet clusters across three months of intent-routed flow using the same tagging approach I applied to NFT wash trading in 2021 — the analysis that showed sixty percent of high-value sales in a popular collection moved between wallets under one controller, produced a spreadsheet linking deposit addresses, and preceded a twenty percent correction. The pattern here is quieter but recognizable. A small set of solver addresses appears disproportionately often in filled flow while appearing rarely in the quoted set that users can actually observe. Correlation is not causation and I will not claim it is. But a solver that wins more than it quotes is a solver whose quotes are not the thing you are being shown.
The fourth layer is institutional flow data, and here I am more constructive. Daily creation and redemption figures reconcile against on-chain movement with a one-to-two-day settlement lag, and the correlation I flagged in 2025 between sustained net inflows and declining exchange reserves has held. But the metric everyone quotes is the daily net number, and that number is an accounting artifact of the creation basket, not a statement of conviction. Inflows are a flow. Reserves are a stock. Confusing a flow for a stock is the most expensive arithmetic error in this market.
Risk in this article is not a headline. It is a matrix. For every data dependency above I now score four properties: provenance, meaning can I reach the block hash; redundancy, meaning how many independent sources; failure mode, meaning does it error or does it return null; and blast radius, meaning what breaks if it lies. A feed with one provider and a null-returning failure mode is a different instrument from a feed with four providers and hard errors, even when both publish an identical number today.
Contrarian
The industry's default explanation for a bad liquidation cascade is manipulation. Someone moved an oracle. Someone ran the price. It is a satisfying story because it supplies a villain.
The data does not support that as the modal case. In the incidents I reconstructed, the majority were not adversarial. They were architectural. A provider returned null, a client coerced it to zero, a comparison passed, and an execution proceeded that no human would have approved had the field read unknown instead of zero. Corruption requires intent. Nulls require nothing but neglect.
The deeper contrarian point concerns transparency itself. We have spent a decade telling people that public ledgers make this industry more honest than finance. That claim is true about the ledger and false about everything layered on top of it. The ledger is transparent. The subgraph is a paraphrase. The API is an interpretation. The dashboard is a sales document. Data is the only witness that cannot be bribed — but it can be paraphrased, and a paraphrase presented as testimony is how an honest ledger produces a dishonest conclusion.
And no, more data does not fix this. Every additional indexer is another component capable of returning an empty set without raising an exception. Redundancy only helps if you reconcile. If you do not reconcile, you have not added a source of truth. You have added a source of agreement.
Takeaway
Watch the spread, not the level. Next week, compare two things you are probably not comparing: indexed block height from every subgraph you depend on, graphed against the chain head, and the identical read call executed across at least three independent node providers at the same block height. Where they disagree, one of them is wrong, and neither of them knows it.
Then answer a question with no comfortable response. If your position had gone underwater during a forty-three-minute indexing lag, would your system have told you — or would it have told you that everything was fine?