Data Quality and Observability
API observability metadata is surfaced in the wallet UI via an observability badge and a data-quality strip to provide users with clear insights into data provenance, staleness, partial data, and fallback states. This is the trust model reference for the Wallet Graph (Holder Wall) and Stable Seer features.1. Wallet Graph / Holder Wall
Endpoint
WalletGraph v1 response contract
Every successful response includescontractVersion: "walletGraph.v1". This field is the authoritative version tag — never remove or rename it.
Key top-level fields:
Provider selection
- No API keys
- ETHERSCAN_API_KEY set
- ALCHEMY_API_KEY only
- Production, no keys
Condition: no
ETHERSCAN_API_KEY and no ALCHEMY_API_KEY.- Provider: Mock provider
observability.source:mockobservability.provider:mock
ethers.id. It never calls external APIs.
Per-wallet provider roles (post-Dune pivot)
Per-wallet features are provider-native, not Dune-backed: parameterized per-wallet Dune queries were incompatible with the read-only cache model, so wallet-detail data now composes from sources the route already reaches directly:
Portfolio value scope is explicit:
valueScope: priced means ETH + priced token
holdings; valueScope: eth_only means token balances were unavailable for this request
and the UI labels the total accordingly. The wallet route makes three Alchemy calls
(native balance, token balances, contract code) plus a gas snapshot — an earlier unused
NFT call was removed.
What is sent to a data provider, and what each one supplies
Looking up a wallet sends that wallet address to Alchemy from WalletWall’s
servers — never from your browser — so the current chain state for it can be read.
When you search an ENS name, the name is resolved to an address and that address is
sent the same way. No other information about you is included in the request.
Two things follow from that separation, and both are enforced rather than promised:
- A cached result is never labelled live. Every served value carries its own source state and cache time. A value read from cache — including a stale value served because a provider was unavailable — is reported as cached, with the instant it was actually written. It is not re-stamped with the time you asked for it.
- Naming a provider is attribution, not endorsement. WalletWall does not claim any partnership with, or approval by, the providers it names, and makes no real-time-accuracy guarantee for any figure. Chain state can change between the observation and the moment you read it, which is why the observation time is shown rather than assumed.
Observability and dataQuality fields
Every normalized response carries two overlapping representations of the same underlying quality signals:observability (structured, always present) and
dataQuality (legacy booleans and flags, preserved for compatibility).
observability fields:
provider: normalized provider name.source: data class —live(real provider data),mock(demo/no keys), orfallback(price fallback active).partial: true when any provider sub-request failed or transaction data is sampled.fallback: true when fallback pricing or demo data is being served.providerErrors: true whenapiErrorscontains entries withseverity === 'partial'or'error'.freshness: wallet’slastActiveISO date string, ornull.durationMs: total route wall-clock time in milliseconds.timing: sub-phase breakdowns. Allowed keys:totalMs,providerMs,coingeckoMs,graphMs,duneMs.
dataQuality flags:
partial: set when data is sampled or any provider sub-call degraded.sampled: set whentransactionSample.isSampledis true.fallback: set when fallback pricing is active.demo: set only for explicit mock/demo data.providerErrors: set whenapiErrorscontainspartialorerrorentries.
apiErrors sanitization
apiErrors entries are user-safe. Before serialization, the error-message sanitizer:
- Strips URL query strings (which may contain provider API keys or tokens) — replaced with
[params redacted]. - Caps message length at 200 characters.
apiErrors.
This invariant applies to every API route, not just the wallet route: on a
cache-miss provider or store failure, routes return a generic warning (e.g.
“market data temporarily unavailable”) — internal error text such as host names,
DNS errors, or store URLs is logged server-side only, never interpolated into the
client response.
UI status badge behavior
Observability badge
Observability badge
Compact badge rendered in the wallet header:
Data-quality strip
Data-quality strip
Fixed bottom bar:
- Reads from
observabilitywhen present; falls back to legacydataQualityfields. - Invariant copy: “Read-only public wallet data · No wallet connection required”
- Conditional: partial note, “Some provider data unavailable”, “Fallback data source”, “Demo mode”, provider name, freshness, durationMs.
Try it — how these flags become a claim
Thepartial, fallback, and throttled flags above are inputs, not conclusions. This
explorer shows what the Wallet Evidence Model does
with them — including why a throttled provider yields an explicit gap rather than a $0,
and why a labelled fallback can lower confidence but never raise it.
2. Stable Seer
Endpoint
holderAnalyticsSupported: false flag is present on every response.
Cache behavior
Cache key is the lowercased query string. Provider error and 4xx/5xx responses are never cached.
confidence values
confidence: "low" on success is intentional — market data is DEX pool data, not audited fundamentals.
confidence: "unavailable" is a provider failure state. UI should suppress user-blame wording when this value is present.
3. Source confidence map
Positions each data source by how live it is and whether it is wallet-specific or context-only. This is not a ranking of importance — a context-only source can still be essential for pricing or peg context. Etherscan and Alchemy are live, wallet-specific sources — transactions, transfers, and token balances for the address being viewed. DeFiLlama and CoinGecko are live but context-only — they price tokens; they do not enumerate a wallet’s holdings. Dune is always scheduled/cached, never live, and covers both wallet-specific and aggregate queries depending on the surface. See the Wallet Evidence Model for how these sources roll up into per-claim status.4. Agent guardrails
Rules enforced by the test suite:- Do not conflate Holder Wall and Stable Seer. These are separate features with separate data sources and API routes.
- Do not invent holder data.
holderAnalyticsSupported: falseon all Stable Seer responses is a hard invariant. - Do not expose secrets. The error-message sanitizer strips query strings from error messages. Tests verify no provider key strings appear in normalized output.
- Keep partial provider degradation non-fatal and non-scary. A failed sub-provider call sets
partial: trueand contributes anapiErrorsentry. It does not fail the route. UI copy should say “Provider data may be partial” — not “Error” or “Failed”. - Preserve existing response contracts.
contractVersion: "walletGraph.v1"must be present on every/api/walletresponse. Theobservabilityfield must be present on every normalized response.
Implementation layers
Behind these contracts, a shared normalization layer handles response normalization, observability metadata, and error-message sanitization. The/api/wallet route handles provider selection, rate limiting, and cache headers, drawing on a provider layer that supplies the mock and live wallet providers (Etherscan, Alchemy, CoinGecko, Dune, and graph sources). The /api/stable-seer route handles DEX Screener fetches backed by a Redis-plus-in-memory cache (900 s / 15 min TTL). In the UI, the observability badge renders a compact provenance indicator in the wallet header and the data-quality strip renders a fixed-bottom provenance bar.
Related
Wallet Graph Contract
The full
/api/wallet response contract, including the dataQuality and transactionSample field reference.Wallet Evidence Model
How these provider sources roll up into per-claim confidence and status.

