JarValley

Market Prices

BTC Bitcoin
$81,604.5 +0.48%
ETH Ethereum
$2,406.21 +1.12%
SOL Solana
$104.45 +0.73%
BNB BNB Chain
$745.7 +2.40%
XRP XRP Ledger
$1.31 -0.15%
DOGE Dogecoin
$0.0799 +1.81%
ADA Cardano
$0.2156 +1.60%
AVAX Avalanche
$9.13 +0.18%
DOT Polkadot
$1.12 +0.42%
LINK Chainlink
$12.03 +1.16%

Event Calendar

{{ๅนดไปฝ}}
08
04
upgrade Solana Firedancer

Independent validator client goes live on mainnet

18
03
unlock Sui Token Unlock

Team and early investor shares released

15
04
halving Bitcoin Halving

Block reward reduced to 3.125 BTC

10
05
upgrade Ethereum Pectra Upgrade

Raises validator limit and account abstraction

22
03
unlock Optimism Unlock

Circulating supply increases by about 2%

28
03
unlock Arbitrum Token Unlock

92 million ARB released

12
05
halving BCH Halving

Block reward halving event

30
04
upgrade Celestia Mainnet Upgrade

Improves data availability sampling efficiency

Tools

All โ†’

Altseason Index

41

Bitcoin Season

BTC Dominance Altseason

Market Cap

All โ†’
# Coin Price
1
Bitcoin BTC
$81,604.5
1
Ethereum ETH
$2,406.21
1
Solana SOL
$104.45
1
BNB Chain BNB
$745.7
1
XRP Ledger XRP
$1.31
1
Dogecoin DOGE
$0.0799
1
Cardano ADA
$0.2156
1
Avalanche AVAX
$9.13
1
Polkadot DOT
$1.12
1
Chainlink LINK
$12.03

๐Ÿ‹ Whale Tracker

๐Ÿ”ด
0xbd9b...d953
12h ago
Out
2,616,009 DOGE
๐ŸŸข
0x3f49...468e
1h ago
In
42,170 BNB
๐ŸŸข
0x8de1...4b63
12h ago
In
2,988 ETH
AI

Null Is Not Zero: The Fail-Open Bug Eating On-Chain Risk Pipelines

Raytoshi

The pipeline refused to answer. That was the anomaly.

I fed it a payload with an empty information_points array โ€” no title, no source, no project, no timestamp, nothing but structure and a heading. It came back with a clean rejection and an error code. Correct behavior. Textbook.

Then I deleted the paragraph of English prose that told it to refuse, and ran the same payload through the scoring layer underneath. It returned risk_level: "low". Composite score: 0. Confidence: 0.82.

Nobody changed the data. Nobody swapped the model. I removed one instruction that was quietly doing the work of a type system, and a system holding zero facts about a token concluded the token was fine. I have spent nine years reading post-mortems and I can tell you this is not an LLM story. It is a [].every() story. The array was empty, the predicate was "is this safe," and JavaScript โ€” like every language that implements universal quantification honestly โ€” returned true.

Code doesn't lie. It just answers the question you actually asked.

Context: the research stack became a pipeline while nobody was watching

Between 2024 and 2026, crypto due diligence industrialized. What used to be a analyst with a spreadsheet became an ingest-decompose-score stack: pull the source document, break it into structured information points, run each point against a fixed framework โ€” technical, tokenomics, market, ecosystem, regulatory, team, risk, narrative, supply-chain transmission โ€” and emit a normalized verdict an allocator can sort on.

I have built three of these. In 2021 I spent eight months manually verifying zk-SNARK constraint systems for a Layer-2 team, and the deliverable was not a proof, it was a checklist. In 2024 I ran Celestia's blob-sidecar in a personal testnet for 200 hours and produced a benchmark harness that compared data-availability throughput against Ethereum. In 2025 I built a ZK verifier for local LLM outputs to block prompt injection in decentralized agents โ€” 99.9% verification accuracy, minimal gas. In all three cases the cryptography was the easy part. The hard part, every single time, was the input contract: what the system is allowed to believe before it starts reasoning.

The framework itself is fine. The nine dimensions are a reasonable decomposition of risk. The problem is structural: every dimension's conclusion is a function of the information points, and the domain of that function is assumed to be non-empty. Nothing in the framework checks the assumption. When the array is empty, the function doesn't return an error. It returns zero, and zero renders as a verdict.

And there is no verdict for "I don't know." Look at the enum. risk_level: low | medium | high. Three states. That is a stronger claim than any model you will ever run โ€” it asserts that every asset in the observable universe can be classified into one of three buckets. The refusal I saw in the first test was not a property of the architecture. It was a property of a sentence I wrote that day.

Core: the empty set satisfies every universal quantifier

Three lines. Three different ways to say "clean" about nothing.

[].every(p => p.safe)      // true
[].some(p => p.risky)      // false
[].reduce((a, b) => a + b.weight, 0)  // 0

A risk section typically asks one of two questions. "Are there red flags?" โ€” implemented as points.some(isRedFlag), which returns false on an empty array, rendered as no red flags found. Or "are all points acceptable?" โ€” implemented as points.every(isSafe), which returns true on an empty array, rendered as all clear. Both answers are formally correct. Both are operationally catastrophic. The universal quantifier is vacuously true over the empty set, and English does not have a word for the difference between "I checked everything and it passed" and "there was nothing to check."

Code doesn't lie. It answers precisely. The lie is in the translation layer we wrote between the sentence and the program.

Null Is Not Zero: The Fail-Open Bug Eating On-Chain Risk Pipelines

The fix is not clever. It is a cardinality guard before the quantifier:

if (!Array.isArray(points) || points.length === 0) {
  throw new Error('INSUFFICIENT_INPUT');
}
const flags = points.filter(isRedFlag);

Which brings us to the data layer, where the bug actually lives. In JavaScript, Number(null) === 0, null 0positive gates1negative filters* (if (tvl < MIN_TVL) reject) lets it through untouched. Most risk pipelines are written as filters, because filters compose better and read cleaner in a diff. In Python the same fork appears as if not points: โ€” which catches both None and [] โ€” versus if points is None:`, which catches only the first. I have reviewed production code containing the second form more times than I can count. It is never a mistake anyone notices, because the branch it fails to take is the branch that would have stopped the report.

SQL is worse. NULL propagates through three-valued logic. WHERE risk != 'high' silently excludes every row where risk is NULL. Your coverage report says 4,000 tokens assessed. Nine hundred of them had no risk field. They are absent from the output, and absence looks exactly like coverage.

The on-chain version costs real money

This is not an analytics-only pathology. It is the same failure class that empties lending pools.

Chainlink's latestRoundData() returns five values. answer can be zero or negative. updatedAt can be stale. The canonical omission is skipping the answeredInRound >= roundId check and skipping the staleness window. An aggregator that computes a median over an empty or partial report set produces a degenerate value, and downstream code asks if (price < liquidationThreshold) liquidate. Zero is a perfectly valid input to a liquidation engine. The consumer has no way, at the call site, to distinguish "this asset is worthless" from "we have no data on this asset." That indistinguishability is the vulnerability, not the price.

Null Is Not Zero: The Fail-Open Bug Eating On-Chain Risk Pipelines

ERC-20 has the same shape. Non-standard tokens return empty returndata with success = true from a low-level call. A contract that checks only the success flag and treats empty returndata as a zero balance will liquidate a fully collateralized position. This is the entire reason SafeERC20 exists โ€” not because some tokens are quirky, but because "no answer" and "answer: zero" were never separated at the ABI boundary.

Access control. require(approvals.every(isApproved)) over an empty approval set returns true, and everyone is approved. A threshold-zero multisig is the same bug with better branding. An EnumerableSet of validators cleared by an upgrade is the same bug with a governance vote attached.

Merkle proofs. A single-leaf tree has root == leaf. An empty proof array verifies against that root. If the verifier does not bind depth, verify(root, leaf, []) returns true for any leaf equal to the root โ€” and if the root is ever settable to a user-controllable value, you have a mint.

Zero-knowledge. A circuit signal with no constraint is a free variable. In 2021, working through constraint systems line by line, the bugs I found were never "the math is wrong." They were "the math is right about fewer things than you think." An unconstrained public input is an empty information point with a proof stapled to it. Same failure, better branding, higher audit invoice.

Benchmarks lie the same way

When I benchmarked the blob-sidecar against Ethereum, I found a 40% reduction in finality time for specific workloads. The number I care about more is the one I nearly published: a control run that produced zero finalized blocks. Averaging over an empty sample yields NaN. In JavaScript, NaN > threshold is false. So the regression check โ€” written as if (delta > tolerance) alert โ€” reported no regression. It reported no regression because it reported nothing, and nothing renders as green.

I caught that one because I was staring at the block explorer. A CI job would not have caught it. Code doesn't lie, but it will happily return NaN and let your dashboard call it a pass.

The schema is a security parameter

nullable: true is not a formatting choice. Every nullable field is a door through which "unknown" enters the system wearing a value's clothes. The design I now use has two fields where most schemas have one: value and provenance. value: null is legal. provenance must be non-null and must name a source and a timestamp. A field with no provenance is not data โ€” it is an assertion with a type annotation, and it should be dropped before the point count is incremented.

Apply that to the two attributes this market cares about most. Layer-2 "decentralized sequencing": in every schema I have seen, it is a boolean or a short string, populated by the team being described, with no forced-withdrawal test in the pipeline and no exit-window measurement behind the field. The pipeline believes it has information. It has a marketing sentence. Second: tvl_usd. There is no tvl_subsidized_usd column. Emissions-driven deposits and organic deposits land in the same number, and the "real users" score is computed from a figure that includes everyone who is there for the yield. Pull the incentives and the column empties โ€” and then, per the section above, the pipeline reports no risk.

Contrarian: the audit budget went to the wrong end of the pipe

Everyone is auditing the model. Almost nobody is auditing the input contract.

Through 2025 and 2026 this sector spent enormous sums on prompts, guardrails, red-teaming, evals, and safety review. All of it sits downstream of the array. If the array is empty, the guardrail is guarding an empty room, and it is doing so with perfect fidelity. I have watched teams produce forty-page adversarial evaluations for a system whose input validator does not check Array.isArray.

Second blind spot: the incentive points the wrong way. A dashboard that says UNKNOWN does not get renewed. Coverage is sold by the row, and a row that reads "we cannot assess this" is a row the client paid for and did not receive. So the pressure is always toward populating the field, and null becomes 0 in production not through a bug but through a product decision made by someone who never opened the schema.

Third: the false negative is invisible by construction. A false positive generates a support ticket within the hour. A false negative generates a wire transfer. You will never receive a bug report for a risk that was never flagged. You will receive a post-mortem nine months later stating that monitoring showed no anomalies โ€” and monitoring will have been correct. There were no anomalies in its input, because there was no input. The empty set passed every check you gave it.

Takeaway

The next nine-figure loss in this industry will not be a reentrancy bug or a compromised key. It will be a null that got coerced somewhere between an ingestion job and a liquidation engine, and the post-mortem will contain the phrase "the system reported no issues."

Somewhere right now a pipeline is scoring a freshly funded project โ€” the kind with $100M, three paragraphs of documentation, and a landing page โ€” and one field in its record is empty, and the score is fine. The exploit is already in the schema. It simply has not been priced yet.

Null Is Not Zero: The Fail-Open Bug Eating On-Chain Risk Pipelines

So: when your risk system returns no findings, do you actually know whether it looked โ€” or whether it had nothing to look at?

Fear & Greed

50

Neutral

Market Sentiment

Gas Tracker

Ethereum 28 Gwei
BNB Chain 3 Gwei
Polygon 42 Gwei
Arbitrum 0.5 Gwei
Optimism 0.3 Gwei

๐Ÿ’ก Smart Money

0xf293...1e4c
Institutional Custody
+$0.7M
81%
0x53fb...cb78
Arbitrage Bot
+$3.5M
66%
0x743d...7ee6
Early Investor
+$0.7M
69%