Wallet → Cluster Revenue Attribution Spec
How to map 30-day incoming USDC transfers on endpoint pay_to wallets to endpoint clusters when one wallet serves many endpoints — often at different prices.
This spec defines max potential attribution: an optimistic per-cluster upper bound that avoids pretending we know more than the chain data allows.
In x402DiscoveryAnalysis: endpoints, cluster assignments, and wallet transfers are read from Postgres. Wallet sync (wallet-sync worker / x402-wallet-sync) incrementally ingests chain transfers into global wallet_transfers (deduped by (wallet, tx_hash)), with per-wallet block cursors in wallet_sync_state. Attribution projection (analytics worker) runs bounds → certainty → revenue over a rolling time window (WALLET_VOLUME_WINDOW_DAYS), triggered debounced on cluster.run_completed and wallet.volume_completed — it never calls Alchemy. Results persist in cluster_revenue_bounds, wallet_plausibility, endpoint_certainty, and cluster_descriptions. Legacy one-shot seed: x402-wallet-fetch. CSV export via scripts/export_flat.py remains an optional escape hatch.
Problem
We have three data sources:
| Source | Key fields | Role |
|---|---|---|
Postgres (endpoints, endpoint_text, price_snapshots, cluster assignments) |
pay_to, cluster_label, effective price, … |
Which endpoints exist and what they cost |
Postgres (wallet_transfers via wallet_volume_runs) |
wallet, value_usdc, tx_hash, … |
Individual incoming USDC transfers |
Aggregated wallet stats (derived from wallet_transfers) |
wallet, incoming_usdc, incoming_tx_count |
Per-wallet aggregates for proportional attribution |
A wallet is the on-chain recipient (pay_to). Many endpoints can share one wallet. Incoming transfers do not say which endpoint was paid.
Proportional approach (x402-revenue / attribute_revenue): split each wallet's volume proportionally by endpoint count per cluster. Simple, but wrong when endpoints on the same wallet have different prices — a $1.00 payment is attributed partly to $0.001 endpoints that could never have received it.
Price-matched approach (x402-bounds): narrow attribution using tx value vs endpoint price, then split within the matching price bucket. Better, but still ambiguous when many endpoints share the same (pay_to, price) across multiple clusters (common for CDP-style catalogs).
Max potential (this spec): don't pick a single split when we can't. Report how much volume could have landed in each cluster, given price plausibility.
Does max potential make sense?
Yes. It matches what we actually know:
- A tx of $0.05 to wallet
Wcannot have paid a $1.00 endpoint onW. - It could have paid any endpoint on
Wlisted at $0.05 (exact scheme, Base USDC). - If 50 clusters each have at least one $0.05 endpoint on
W, we cannot say which cluster earned it — but we can say each of those 50 clusters is plausible, and each could have received the full $0.05.
Max potential is a per-cluster upper bound, not a partition of revenue. Summing max potential across all clusters will exceed total chain volume whenever ambiguity exists. That is expected and useful.
Use it to answer: "Could this cluster plausibly explain this much money?" not "This cluster definitely earned exactly $X."
Definitions
Endpoint price
For each endpoint row:
- Use
price_usd_effective, elseprice_usd(seeanalytics/pricing.py). - Normalize to 6 decimal places (USDC on Base).
- Optionally restrict to exact-scheme accepts only (see Price index).
Skip endpoints with no usable price (~11 rows today).
Price index
Build once from endpoint rows (load_endpoint_rows → build_price_index):
pay_to → price → [EndpointRef…]
pay_to → price → set(cluster_label)
Precompute for each (pay_to, price):
endpoint_countcluster_set— distinct clusters with ≥1 endpoint at that pricesingle_cluster— true whenlen(cluster_set) == 1
Plausible clusters for a transaction
For incoming tx (wallet, value):
- Exact match:
valueequals a listed price forwallet. - Near match (optional): nearest listed price within
--price-tolerance(default0.000001USDC). - No match: fall back to wallet-wide plausibility.
Plausible cluster set = all clusters that have ≥1 endpoint on wallet at the matched price.
If near match ties (two prices equally close), treat as no price match → fallback.
Attribution modes
We report several stats side by side. They answer different questions.
| Mode | Per-tx rule | Sums across clusters | Interpretation |
|---|---|---|---|
| Proportional (existing) | Split by endpoint share across all wallet endpoints | ≈ total volume | Neutral guess; smears across wrong prices |
| Max potential | If cluster ∈ plausible set, add full value |
≥ total volume | Optimistic upper bound per cluster |
| Min potential (guaranteed) | Add value only if plausible set has exactly one cluster |
≤ total volume | Pessimistic lower bound; only certain cases |
| Expected (price bucket) | Split value evenly among endpoints at matched price, then by cluster |
≈ total volume | Best point estimate when implementing splits |
Max potential is the headline stat for exploratory cluster ranking under ambiguity.
Max potential algorithm
Stream wallet transfers from Postgres (wallet_transfers, ordered by wallet).
For each tx with wallet, value (USDC):
plausible = clusters_plausible(wallet, value)
if plausible is empty:
# Fallback: any cluster with any endpoint on this wallet
plausible = all_clusters(wallet)
match_type = "fallback"
for cluster in plausible:
cluster_max_potential_usdc[cluster] += value
cluster_max_potential_txs[cluster] += 1 # full tx counted per plausible cluster
track match_type counts (exact / near / fallback)
Important: do not divide value among plausible clusters. Each plausible cluster gets the full amount. Double-counting across clusters is intentional.
Derived cluster fields
max_potential_usdc_30d
max_potential_tx_count_30d
max_avg_tx_usdc_30d = max_potential_usdc / max_potential_tx_count
certain_usdc_30d # min potential alias
certain_tx_count_30d
ambiguous_usdc_30d # max - certain (per cluster, see note below)
plausible_cluster_count # avg |plausible set| for txs touching this cluster
Note on ambiguous_usdc at cluster level: ambiguity is a property of a transaction, not cleanly decomposable per cluster. Prefer wallet-level ambiguity stats; at cluster level, report certain_usdc vs max_potential_usdc as bounds.
Wallet-level max potential
Same logic keyed by wallet instead of cluster:
wallet_max_potential_usdc= sum of tx values (always equals actual wallet volume).- Useful for sanity checks, not cluster ranking.
Fallback: no price match
When tx value matches no listed price for that wallet:
- Max potential: every cluster with any endpoint on that wallet is plausible (widest bound).
- Min potential: $0 added (we cannot prove any specific cluster).
- Expected: revert to proportional split across all wallet endpoints (same as proportional revenue).
Log these txs; they dominate error when prices are stale or non-exact schemes are excluded.
Worked examples
Example A — Two prices, two clusters (clean case)
Wallet W has:
- Cluster 1: one endpoint at $0.01
- Cluster 2: one endpoint at $1.00
| Tx value | Plausible clusters | Max potential added |
|---|---|---|
| $0.01 | {1} | C1 += $0.01 |
| $1.00 | {2} | C2 += $1.00 |
Min potential = max potential. No ambiguity.
Example B — Same price, multiple clusters (catalog case)
Wallet W has 100 endpoints at $0.05 spanning clusters {A, B, C, …, Z}.
Tx: $0.05
- Plausible clusters: all clusters with a $0.05 endpoint (say 20 clusters).
- Max potential: each of the 20 clusters += $0.05 (20 × $0.05 = $1.00 total upper-bound mass).
- Min potential: each cluster += $0 (cannot prove which one).
- Expected: each cluster += $0.05 / (100 endpoints) × (endpoints that cluster has at $0.05).
Max potential tells you: "Any of these 20 themes could be driving $0.05 payments at this price point."
Example C — Proportional vs max (why this matters)
Wallet W: 999 endpoints at $0.001 (cluster Noise), 1 endpoint at $1.00 (cluster Premium).
One tx: $1.00
| Mode | Noise | Premium |
|---|---|---|
| Proportional | ~$0.999 | ~$0.001 |
| Max potential | $0 | $1.00 |
| Min potential | $0 | $1.00 |
Proportional ranks Noise as the revenue driver. Max potential (and min, here) correctly identifies Premium as the only plausible recipient.
Data landscape (current export)
From endpoint export (approximate):
| Metric | Count |
|---|---|
| Wallets with 2+ endpoints | 372 |
| Wallets with 2+ distinct prices | 273 |
(pay_to, price) pairs with 2+ endpoints |
715 |
(pay_to, price) pairs spanning 2+ clusters |
525 |
Max potential helps most for multi-price wallets (Example C). For same-price / multi-cluster wallets (Example B), max potential stays wide — pair it with certain_usdc and cluster endpoint counts to interpret.
Outputs
cluster_revenue_bounds (Postgres)
| Column | Description |
|---|---|
cluster_label |
Cluster id (-1 = noise) |
max_potential_usdc |
Upper-bound USDC |
max_potential_tx_count |
Upper-bound tx count |
certain_usdc |
Min potential (unique plausible cluster only) |
certain_tx_count |
Tx count for certain cases |
expected_usdc |
Price-bucket proportional estimate |
proportional_usdc |
Existing split (for comparison) |
endpoint_count |
Endpoints in cluster |
analytics_runs.params_json (bounds job)
Run-level summary stored on the analytics run:
- Total txs / USDC processed
- Match rates: exact / near / fallback (% of USDC)
- Unmatched wallet tx counts
wallet_plausibility (Postgres)
Per wallet:
incoming_usdcdistinct_pricescertain_tx_count/pct_certaintop_ambiguous_prices— prices that map to many clusters
CLI
x402-wallet-fetch --local
x402-bounds --local `
--price-tolerance 0.000001 `
--exact-scheme-only `
--no-near-match
x402-certainty --local --min-certain 0.01
x402-revenue --local --describe-top 50
Flags (x402-bounds):
| Flag | Purpose |
|---|---|
--exact-scheme-only |
Only index exact-scheme USDC accepts |
--price-tolerance |
Near-match epsilon |
--no-near-match |
Exact price only |
--cluster-run-id |
Pin cluster run (default: latest) |
--wallet-volume-run-id |
Pin wallet fetch run (default: latest completed) |
Requires wallet transfers in Postgres (x402-wallet-fetch first).
Implementation
Code lives in src/x402_discovery/analytics/:
wallet_attribution.py— price index, max/min/expected/proportional attribution.bounds.py— stream transfers from DB, persist bounds + plausibility.certainty.py— endpoint-level certainty rankings.revenue.py+gemini.py— proportional revenue + cluster descriptions.
Unit tests: tests/test_wallet_bounds.py (Examples A–C).
Interpretation guide
| Question | Stat to use |
|---|---|
| Could this cluster be a major revenue driver? | max_potential_usdc |
| Do we know this cluster earned this? | certain_usdc |
| Best single-number estimate | expected_usdc (price bucket split) |
| Compare to old analysis | proportional_usdc |
| How ambiguous is a cluster? | max_potential / certain (when certain > 0); or high max with certain ≈ 0 |
When presenting externally:
- Report ranges (
certain–max) for high-value clusters. - Do not sum
max_potentialacross clusters and call it "total revenue." - Rank clusters by
max_potentialfor discovery; confirm withcertainor manual sampling.
Edge cases
| Case | Max potential behavior |
|---|---|
| Unknown wallet (tx but no endpoints) | Skip; log as unmatched |
| Endpoint missing price | Excluded from price index; may force fallback |
| Non-exact scheme (batch-settlement) | Exclude from index unless flag off; likely fallback |
| Stale price vs actual payment | Near match or fallback; flag in stats |
| Duplicate endpoints same price same cluster | Collapse in index (set semantics) |
| Multiple txs same second | Each evaluated independently |
Relationship to other commands
x402-wallet-fetch → wallet_transfers (Postgres)
x402-revenue → proportional + Gemini descriptions
x402-bounds → max / min / expected (this spec)
x402-certainty → endpoint-level certainty rankings
Max potential does not replace proportional attribution; it complements it with honest uncertainty bounds.
Open questions
- Near-match tolerance: Is
1e-6USDC enough, or do we see systematic drift (e.g. fees)? - Exact-scheme-only default: On or off for v1?
- Gemini copy: Descriptions say "up to $X" when
certain << max(implemented ingemini.py). - Fetch completeness: Bounds job warns when wallet coverage is incomplete.
Glossary
| Term | Meaning |
|---|---|
| pay_to / wallet | On-chain USDC recipient for x402 payments |
| Price bucket | All endpoints on a wallet sharing one listed price |
| Plausible cluster | Cluster with ≥1 endpoint whose price matches the tx |
| Max potential | Full tx value credited to every plausible cluster |
| Min potential / certain | Full tx value credited only when exactly one plausible cluster |
| Expected | Proportional split within the price bucket, then by cluster |
| Fallback | No price match; use all wallet clusters (max) or proportional (expected) |