mirror of
https://github.com/luxfi/graph.git
synced 2026-09-24 00:26:07 +00:00
Every USD field the exchange reads was an empty string. `pools`, `tokens` and
`factories` all resolved — with real addresses, real symbols, real fee tiers —
and then reported totalValueLockedUSD:"", volumeUSD:"", token0Price:"". The
landing page's TVL / 24h-volume / active-pool header had nothing to render, so
it rendered nothing.
Nothing was ever writing those fields. processLog's Sync/Mint/Burn arm was an
explicit no-op ("storage for these types not yet wired"), and bumpFactory's
comment deferred the USD aggregates to a "price oracle / day data" that does not
exist. Sync is the only event carrying V2 reserves, so with it inert there was
no path — at any block height — from the chain to a TVL number.
Fixed by adding the missing concern rather than by decoding one more event:
indexer/valuation.go is the ONE owner of the derived USD aggregates. It reads
balanceOf(pool) for both legs of every registered pool in a single JSON-RPC
batch, anchors stablecoins at $1 by symbol, and relaxes prices outward through
the pool graph — each unknown token taking its quote from the DEEPEST pool
that already has a priced side, which is both deterministic and the least
manipulable choice when pools disagree. A token no path connects to a stable
stays unpriced and contributes nothing: unknown is reported as unknown.
Reserves are chain STATE, not chain HISTORY, so this is a pass beside the log
poller, not a handler inside it. That distinction is the whole point:
- Sync exists only on V2. balanceOf answers the same question for V2, V3 and
V4 identically.
- An event-sourced TVL only converges by replaying all history, and a pool
whose last Sync is a million blocks back reads empty until it trades again.
On an idle chain it never trades again.
- It is one fact, so it gets one writer. The Sync/Mint/Burn arm stays inert on
purpose and now says why.
poolCount moves to the same pass for the same reason: it is derived from the
store, not accumulated by the create handlers, so it can no longer drift across
a restart, a re-index or a duplicate create. bumpFactory keeps the one field
that genuinely is a running total of events — txCount.
Fails closed: an unreachable RPC aborts the pass rather than overwriting good
aggregates with zeros.
Tests: valuation_test.go drives a stub RPC shaped like the live mainnet book —
a stablecoin pair anchoring a WLUX pair — and asserts derived TVL, spot prices,
per-token TVL, factory poolCount, the deeper-quote rule, non-clobbering of
fields the pass does not own, and the RPC-blip guard. Full suite green.
Co-authored-by: Hanzo Dev <[email protected]>