Skip to main content

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

All responses pass through a shared normalization layer before the route sends JSON.

WalletGraph v1 response contract

Every successful response includes contractVersion: "walletGraph.v1". This field is the authoritative version tag — never remove or rename it. Key top-level fields:

Provider selection

Condition: no ETHERSCAN_API_KEY and no ALCHEMY_API_KEY.
  • Provider: Mock provider
  • observability.source: mock
  • observability.provider: mock
Mock data is deterministic and address-seeded via 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.
Provider responsibilities are deliberately separated and are not interchangeable: 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), or fallback (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 when apiErrors contains entries with severity === 'partial' or 'error'.
  • freshness: wallet’s lastActive ISO date string, or null.
  • 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 when transactionSample.isSampled is true.
  • fallback: set when fallback pricing is active.
  • demo: set only for explicit mock/demo data.
  • providerErrors: set when apiErrors contains partial or error entries.

apiErrors sanitization

apiErrors entries are user-safe. Before serialization, the error-message sanitizer:
  1. Strips URL query strings (which may contain provider API keys or tokens) — replaced with [params redacted].
  2. Caps message length at 200 characters.
Never add raw provider error messages, API keys, full URLs, or stack traces to 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

Neither the observability badge nor the data-quality strip ever exposes raw URLs, API keys, stack traces, or provider error details.
Compact badge rendered in the wallet header:
Fixed bottom bar:
  • Reads from observability when present; falls back to legacy dataQuality fields.
  • 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

The partial, 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

Stable Seer performs DEX token and pool market lookup using DEX Screener. It does not provide holder analytics. The 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: false on 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: true and contributes an apiErrors entry. 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/wallet response. The observability field 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.

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.