The data layer

Coverage and confidence

Know how much of the chain Roundhouse sees, and which published figures are exact.

Two separate questions decide what a figure here is worth: coverage (did we see it?) and confidence (is it what we think it is?). Blending them produces numbers that look authoritative and are not.

This page is what to believe.

Confidence: is this row really an x402 payment?#

verified_x402 is three-valued, and the three are genuinely different claims:

ValueClaimEvidence
trueThis was an x402 settlementAn EIP-3009 AuthorizationUsed log in the transaction receipt
nullUnexaminedMatched a catalogued payTo; heuristic confidence is low — usually probably not
falseDisprovenExamined, and it was a plain transfer, not an authorized payment

The true case is the only one with proof. A USDC transfer to a known service wallet is evidence of a payment; the EIP-3009 marker is proof that the transfer went through an authorization rather than a plain transfer() call.

sql
-- Say what you mean.
select count(*) from settlements
where payee = lower('0x…') and verified_x402

Warning

mv_entity_rollups — and therefore every lifetime figure on a profile page — sums all three values. The pages that use it say so. When a number needs to mean proven, compute it yourself with the filter above. Fixing the rollups needs new matview columns, and it is known outstanding work rather than a subtlety we are hiding.

/stats leads with the verified population and discloses the rest in words. The splits reconcile exactly against the blended totals, so the view is a decomposition rather than a competing number.

Coverage: did we see it?#

Capture runs continuously against each supported chain, with a cursor per chain. Three mechanisms keep it honest, and they check different directions:

The reorg sweep asks "do we hold rows the chain no longer has?" and deletes them. Base's finality depth is set to 2 confirmations, which is only safe because the 24 blocks behind the cursor are re-verified every run. Settlements are immutable and duplicate inserts are ignored, so a row for a reorged-out block cannot be fixed by re-ingesting — it has to be removed.

The continuity audit asks the mirror-image question: "does the chain have settlements we do not hold?" and queues the range for repair. It compares distinct transactions rather than rows, because a batch relay is one transaction with several settlements and a fee-proxy collapse is two legs folded into one row. It is deliberately conservative: an empty marker set is treated as no information, not as an empty chain.

Gap repair drains the queue through the same path a live settlement takes — marker-verified, collapsed, attributed. A recovered settlement is not a lower-grade row.

Between them, the chain arbitrates our record in both directions.

What /status tells you#

/status is the honest surface, and its verdict reads the whole page rather than just reachability:

SignalMeans
Capture lag per chainHow far behind the tip we are
Open gapsRanges the audit found and repair has not yet drained
Gap count nullThe ledger was unreadable — never "no gaps"
Rollup stalenessHow old the pre-computed aggregates are
Service freshnessWhen the catalog was last crawled and enriched

Open gaps existing is not an incident: the audit finds, repair drains, that is the system working. What is worth alerting on is a range repair gave up on, or one nothing is draining.

Note the asymmetry: capture can be at the tip with a hole behind it, so lag does not imply completeness. That is exactly why the gap count is published separately.

Figures that are exact, and figures that are floors#

FigureStatusWhy
Settlement counts over a bounded rangeExactCounted directly from an index range
Settled volume, anywhereA flooramount_usd is NULL for unpriced tokens
Distinct payers / payeesExact for what is indexedBounded by coverage, not by pricing
Per-entity lifetime totalsBlended confidenceSums true, null and false
Service listing countsExact matched countFrom a counting query, not a fetch window
"Nobody has paid this"Not assertableAbsence of a row is not proof of absence

Two habits behind those rows:

  • A price is never guessed. A NULL usd_rate means captured-but-unpriced. So a count and a sum are computed over different populations, and /stats states the unpriced count.
  • A fetch window is not a dataset. A list that reads 600 rows must not print "600" as a total — the directory shows an exact matched count from a counting query and cites the indexed total separately.

What is not indexed#

  • Chains we do not observe. Coverage is Base and Base Sepolia today. A payment elsewhere is invisible here, and its absence says nothing about the counterparty.
  • Off-chain payments. Invoices, cards, credits. Not our ledger.
  • Testnet as economic activity. Base Sepolia settlements are indexed but are not real volume.
  • Anything private. There is no private tier of the dataset; paid tiers buy rate limit, not access.

Reading a number responsibly#

Three questions, and the answers are all above:

  1. Is it filtered on confidence? If not, it blends proven with unexamined.
  2. Is it a count or a sum? Sums are floors, because unpriced rows are absent from them.
  3. Is the coverage window clean? Check /status for open gaps in the range you care about.

Answering those three is the difference between a figure you can publish and a figure that is approximately right.

Next steps#