> ## Documentation Index
> Fetch the complete documentation index at: https://docs.walletwall.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Wallet Graph Response Contract

> WalletGraph v1 field reference for GET /api/wallet — nodes, edges, dataQuality, observability, and transaction sample.

# Wallet Graph Response Contract v1

`GET /api/wallet?address=<0x-or-ens>` returns a WalletGraph document for the read-only Ethereum wallet explorer. The contract is normalized by a shared normalization layer before the route sends JSON.

## Request flow

```mermaid theme={null}
%%{init: {'theme': 'base', 'themeVariables': {'actorBkg': '#B84923', 'actorBorder': '#6B2412', 'actorTextColor': '#FFF7E8', 'actorLineColor': '#8A5A1F', 'signalColor': '#2B2118', 'signalTextColor': '#2B2118', 'labelBoxBkgColor': '#A7B0B5', 'labelBoxBorderColor': '#59646A', 'labelTextColor': '#172014', 'loopTextColor': '#2B2118', 'noteBkgColor': '#D6A85A', 'noteBorderColor': '#8A5A1F', 'noteTextColor': '#2B2118', 'activationBkgColor': '#FAFAF0', 'activationBorderColor': '#B87333', 'sequenceNumberColor': '#FFF7E8'}}}%%
sequenceDiagram
    accTitle: Wallet Graph request and normalization flow
    accDescr: A client sends GET /api/wallet with an address or ENS name to the API route. The route's raw result passes through a shared normalization layer, which strips the internal _observabilityMeta field, sanitizes apiErrors messages and node labels, and returns the normalized WalletGraph v1 JSON document to the client.
    participant Client
    participant Route as /api/wallet route
    participant Normalizer as Normalization layer
    Client->>Route: GET /api/wallet?address=<0x-or-ens>
    Route->>Normalizer: Raw wallet data
    Normalizer->>Normalizer: Strip _observabilityMeta, sanitize apiErrors + node labels
    Normalizer-->>Route: Normalized WalletGraph v1
    Route-->>Client: JSON response (contractVersion: walletGraph.v1)
```

*The normalization layer sits between the route and the response: it strips internal-only metadata and sanitizes error messages and labels before anything reaches the client. See [Serialization guardrails](#serialization-guardrails) below for the full list of what must never appear in the response.*

## Top-level fields

Required stable metadata:

| Field               | Type               | Notes                                                                                                                        |
| ------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `contractVersion`   | `"walletGraph.v1"` | Stable version tag — never remove or rename                                                                                  |
| `address`           | string             | Resolved Ethereum address. ENS input resolves to 0x when a provider is configured.                                           |
| `chain`             | `"ethereum"`       | Currently Ethereum only                                                                                                      |
| `provider`          | string             | Primary wallet activity provider, e.g. `etherscan`, `alchemy_only`, or `demo`. Falls back to `"unknown"` when not derivable. |
| `source`            | string             | Normalized source class: `live`, `real`, `partial`, or `mock`                                                                |
| `sources`           | object             | Provider map for backwards compatibility. Current fields: `walletActivity`, `prices`                                         |
| `dataQuality`       | object             | Normalized quality object                                                                                                    |
| `isFallback`        | boolean            | Top-level alias for `dataQuality.isFallback`                                                                                 |
| `transactionSample` | object             | Sample metadata for loaded normal transactions                                                                               |
| `apiErrors`         | array              | User-safe provider/config warnings. No provider keys, URLs, or secrets.                                                      |
| `nodes`             | array              | Graph nodes                                                                                                                  |
| `edges`             | array              | Graph edges                                                                                                                  |

Existing wallet metrics remain available for compatibility: `ens`, `ownedENSNames`, `totalValueUSD`, `totalValueEstimated`, `ethBalance`, `firstSeen`, `lastActive`, `txCount`, `overallRiskScore`, `dataConfidence`, `dataSource`, `transactions`, and `fingerprintScore`.

## Fingerprint score

`fingerprintScore.total` and every entry in `fingerprintScore.breakdown` are
**nullable**. Consumers must not assume a number.

```json theme={null}
{
  "status": "scored",
  "total": 63,
  "maxScore": 100,
  "confidence": "high",
  "breakdown": {
    "defiSophistication": 12,
    "protocolDiversity": 13,
    "gasEfficiency": 8,
    "riskManagement": 17,
    "activityConsistency": 13
  },
  "evidence": { "gasEfficiency": { "state": "observed_complete", "consideredRows": 120, "acceptedRows": 120, "excludedRows": 0 } },
  "reasonCodes": [],
  "caveats": []
}
```

| `status`            | `total` | `confidence` | Meaning                                                                                |
| ------------------- | ------- | ------------ | -------------------------------------------------------------------------------------- |
| `scored`            | number  | `high`       | All five dimensions measured on complete coverage.                                     |
| `partial`           | number  | `medium`     | All five dimensions measured, but at least one rests on bounded or truncated coverage. |
| `insufficient_data` | `null`  | `low`        | At least one dimension could not be measured.                                          |

Rules a consumer can rely on:

* **A `partial` result is numeric.** Its `total` is the ordinary sum of the same
  five component formulas — bounded coverage lowers `confidence` and is named in
  `caveats`, it never rescales a component. There is **no renormalization** onto
  a reduced denominator.
* **A subset is never summed.** One to four measured dimensions is
  `insufficient_data` with a `null` total, not a partial score. A behavioural
  fingerprint cannot be assembled from part of itself.
* **Confirmed empty and unavailable are different facts.** Both yield
  `insufficient_data`, distinguished by `reasonCodes`: `wallet_confirmed_empty`
  means the history was read successfully and holds nothing measurable;
  `wallet_evidence_unavailable` means a source did not answer. A confirmed-empty
  wallet may still carry an observed `riskManagement` value.
* **A component of `0` is a real measurement** and must not be treated as absent.
  Only `null` means "not measured".
* **Weights, ceilings, threshold anchors and label bands are unchanged.** The
  eligibility gate decides *whether* a dimension is scored, never *how*.

Per-axis `evidence[axis].state` is one of `observed_complete`,
`observed_partial`, `confirmed_empty`, `unavailable` or `malformed`. It is
**derived** from the canonical evidence-quality fields carried alongside it
(`valueState`, `coverage`, `sampleStrength`, `invariantStatus`), which remain the
source of truth.

### History-leg dependencies

The two transaction-history legs settle independently, and each axis declares
which it needs in `evidence[axis].dependsOnLegs`:

| Axis                  | Depends on         | Why                                                |
| --------------------- | ------------------ | -------------------------------------------------- |
| `gasEfficiency`       | `native`           | Gas prices come only from native transaction rows. |
| `riskManagement`      | `native`           | Anomaly detection inspects native rows.            |
| `defiSophistication`  | `native` + `token` | The node set is assembled from both.               |
| `protocolDiversity`   | `native` + `token` | Ratio denominator includes ERC-20 nodes.           |
| `activityConsistency` | `native` + `token` | Timelines are read off those same nodes.           |

Combined axes join both legs **pessimistically** — the result is never stronger
than either input (`full`+`partial` → `partial`, `full`+`unknown` → `unknown`),
and combined axes report `nativeCoverage` and `tokenCoverage` so the join is
auditable. A **failed** required leg withholds the axis rather than downgrading
it: a missing ERC-20 leg removes whole nodes, which makes the ratio unknown
rather than merely partial. A leg that succeeded and returned nothing is
confirmed-empty, not failed.

Coverage that is **omitted** by the caller is `unknown`, never `full` — silence
is not evidence of completeness. A producer that observed complete windows must
say so explicitly.

A malformed or legacy `fingerprintScore` is normalized to `insufficient_data`
rather than rejected, so a bad cached value degrades the score and never fails
the wallet response.

## Data quality

`dataQuality` preserves legacy booleans and adds explicit flags:

```json theme={null}
{
  "isFallback": false,
  "isDemo": false,
  "isPartial": true,
  "warnings": [],
  "flags": {
    "partial": true,
    "sampled": false,
    "fallback": false,
    "demo": false,
    "providerErrors": true
  },
  "providerErrors": [],
  "updatedAt": "2026-05-01T00:00:00.000Z"
}
```

| Flag             | Meaning                                                                           |
| ---------------- | --------------------------------------------------------------------------------- |
| `partial`        | Route sampled data or a provider/config warning means the graph may be incomplete |
| `sampled`        | `transactionSample.isSampled` is true                                             |
| `fallback`       | Fallback pricing or demo/fallback data is being served                            |
| `demo`           | Explicit mock/demo wallet data                                                    |
| `providerErrors` | `apiErrors` contains `partial` or `error` severities                              |
| `updatedAt`      | Included only when derivable from loaded transaction sample freshness             |

## Transaction sample

`transactionSample` describes loaded normal transactions, not global wallet totals:

| Field             | Description                                                                   |
| ----------------- | ----------------------------------------------------------------------------- |
| `loadedCount`     | Number of normal transactions loaded into this response                       |
| `sampleLimit`     | Configured sample limit, or `null` when no sampling limit applies             |
| `isSampled`       | true when the response hit the configured sample limit                        |
| `firstLoadedTxAt` | ISO timestamp of earliest loaded transaction, or `null`                       |
| `lastLoadedTxAt`  | ISO timestamp of latest loaded transaction, or `null`                         |
| `totalKnown`      | Currently `null` — the route does not claim a provider total it does not have |

## Nodes

Each node is normalized to include:

| Field          | Type   | Notes                                                               |
| -------------- | ------ | ------------------------------------------------------------------- |
| `id`           | string | Stable graph-local identifier, normalized to string                 |
| `label`        | string | Sanitized display label                                             |
| `type`         | string | One of: `wallet`, `token`, `defi`, `nft`, `counterparty`, `anomaly` |
| `volumeUSD`    | number | Uses `0` when unavailable                                           |
| `interactions` | number | Uses `0` when unavailable                                           |

<Warning>
  Node `type` values are constrained to the six values listed above. Do not add new node types without updating this contract.
</Warning>

<AccordionGroup>
  <Accordion title="Optional node fields">
    These fields may be present when derivable: `fullAddress`, `color`, `volumeEstimated`, `riskScore`, `balanceUSD`, `priceUSD`, `firstSeen`, `lastActive`, `protocolAttributionConfidence`, `timeline`, `topCounterparties`, `delta7d`, `anomalies`, `opportunities`.
  </Accordion>
</AccordionGroup>

## Edges

Each edge is normalized to include:

| Field       | Type   | Notes                                                                  |
| ----------- | ------ | ---------------------------------------------------------------------- |
| `source`    | string | Source node id                                                         |
| `target`    | string | Target node id                                                         |
| `weightUSD` | number | USD-like edge weight used by the current graph                         |
| `weight`    | number | Numeric alias, derived from `weightUSD` when no explicit weight exists |
| `txCount`   | number | Numeric interaction count                                              |

Current edge relationships: wallet-to-token, token-to-protocol, and token-to-counterparty, derived from loaded Ethereum wallet activity.

## Serialization guardrails

* `_observabilityMeta` is stripped before the response is sent and must not appear in the response body.
* No provider API key or secret material may appear in any serialized field.
* `apiErrors[].message` is sanitized before the route returns.
* Node `label` values are sanitized — phishing/spam symbols are redacted.

## Non-goals

* No Solana holder analytics are claimed or implied.
* No new provider data is invented.
* No UI layout, loading screen, typography, or graph rendering behavior is part of this contract.
* Do not add new top-level fields to the wallet response without updating this document.

## Related

<Columns cols={2}>
  <Card title="Data Quality and Observability" icon="chart-line" href="/architecture/data-quality">
    The observability metadata, provider-selection logic, and trust model this contract's `dataQuality` fields build on.
  </Card>

  <Card title="Wallet Evidence Model" icon="diagram-project" href="/architecture/wallet-evidence-model">
    The source-aware claims layer that extends this response with `walletEvidence`.
  </Card>
</Columns>
