Methodology
Every formula, every data source, every assumption — auditable end to end.
Data sources
The site stitches three live sources plus a snapshot archive. Every page that displays a derived number can be traced to one of these.
| Source | Use |
|---|---|
| malbeclabs/topology | Devices, links, validators, metros (live) |
| malbeclabs/status | Link health, active issues, top-utilised paths |
| malbeclabs/stats | Validators-on-DZ count, stake share, total bandwidth |
| dz/economic-hub | All-time 2Z distributed, debt, burn, per-contributor reward % |
| DZ contributor-rewards snapshots | Per-epoch S3 snapshots used by the simulator |
| DZ fees CSV | Historical Solana epoch fee totals (859–938) |
| Jupiter price API | Live 2Z and SOL USD spot prices |
Reward share (canonical)
Every contributor's “reward share” on /economics and /contributors comes directly from doublezero.xyz/api/economic-hub's reward_percentage. We do not recompute it client-side. This is the Foundation-published all-time share across the {epoch list} epochs distributed to date.
earned_2Z(operator) = (reward_percentage / 100) × total_distributed_2Z
reward_percentage is Σ paid_to_operator / Σ paid_total across all distributed epochs — confirmed by DZ Foundation 2026-05-12. Not pool-size-weighted; a raw share of cumulative payout.
Reward share (Shapley simulator)
The Forecast tool runs a multi-commodity flow LP per coalition over the snapshot inputs, returning per-operator marginal contribution normalised to a share. When the Rust microservice is configured, we delegate to network-shapley-rs for bit-comparable Foundation output. Otherwise the in-process TS solver is used as a directional fallback.
Input tables (devices, private_links, public_links, demands) are built by a TypeScript port of Foundation's build_shapley_inputs.py reference, verified row-for-row identical to that reference on mainnet epoch 149. The reference is itself verified byte-for-byte against the upstream doublezero-offchain contributor-rewards v0.5.3 Rust binary. Method label canonical-snapshot in the API response indicates the canonical path is active.
coalition_value(S) = max Σ demand_satisfied(d) − contiguity_penalty
subject to per-link capacity, uptime, multicast rules
shapley(op) = Σ_S |S|!·(n−|S|−1)!/n! · [v(S∪{op}) − v(S)]
share(op) = shapley(op) / Σ_op shapley(op)Latest-epoch Shapley anchor
GET /api/shapley/baseline returns the Shapley values for the latest completed epoch (DZ-current methodology), served from the shared per-epoch cache and kept warm by the precompute cron — not an on-demand live-topology solve. Surfaced on /economics under “Latest-epoch Shapley anchor”.
Inputs come from the canonical TS builder when the historical S3 snapshot path is used. Public-link latencies are p95 over the epoch window across all non-RIPE Atlas internet latency providers (averaged per city pair). The public internet is treated as infinite-capacity per DZ Foundation Q16 — only private links have bandwidth constraints.
Results come from the shared per-epoch cache (kept warm by the precompute cron), with a short 5-minute last-good cache in front. The method label in the response is always an lp-* label from the Rust solver in production (currently lp-per-city-stake-weighted-exact); if you see local-ts-heuristic-DEV-ONLY the deployment is missing SHAPLEY_SERVICE_URL and the result is not canonical.
Projected 2Z payout
On /contributors/[code] we project a forward 2Z payout per epoch. The contributor pool is 45% of total fee revenue.
contributor_pool = average_fee_per_epoch × 0.45 per_epoch_2Z = current_share × contributor_pool per_year_2Z = per_epoch_2Z × 144
average_2Z_per_epochis the mean of historical fee CSV totals. This is a directional projection — the validator- paid fee feed is paused, so forward numbers should be read as “at historical rates,” not a rate card.
Validator pool
On /validators we project SOL share of the validator pool (29.25% of total fees, or 32.5% of after-burn fees), stake-weighted across eligible publishers. Per DZ Foundation Q12, eligibility requires publishing_leader_shreds = true AND publishing_retransmitted = false. Anyone failing either condition receives zero. The pool is split 65/35 between validators and their clients respectively — the validator only keeps 65% of their stake-weighted share.
total_validator_pool = average_fee_per_epoch_SOL × 0.45 (incl. clients) validator_pool_per_epoch = total_validator_pool × 0.65 (29.25% of total fees) client_pool_per_epoch = total_validator_pool × 0.35 (15.75% of total fees) operator_share = activated_stake / Σ eligible_stake validator_take_per_epoch = operator_share × validator_pool_per_epoch
multicast_connectedis shown as a quality signal but is not weighted into the projection — Foundation Q12 confirms eligibility is binary on shreds/retransmit, not a multicast multiplier. Publisher set membership is sourced from DZ Foundation's canonical multicast_validators/latest.json feed, enriched with malbec's publisher-check where it adds richer fields (retransmit flag, client version, validator name).
Link Rewards
/link-value shows CANONICAL per-link values only — a faithful port of DZ's network_linkestimate: each focus link is retagged as its own pseudo-operator and an exact coalition Shapley value is solved over the epoch's full demand set. Results are precomputed per epoch (cron sweep) and served from S3; uncached pairs run as a background job with live progress. There is no fallback estimator: if the canonical solve is unavailable the page says so and shows no values.
link_value(L) = shapley_L over players {each focus link, "Others"}
share(L) = max(link_value(L), 0) / Σ max(link_value(·), 0)Per-metro demand
/network's metro-demand panel aggregates live topology by metro: inbound/outbound bps from links, validator count + stake from devices, and presence of multiple contributors as a redundancy signal.
metro_inbound_bps = Σ links where side ∈ metro: link.in_bps metro_outbound_bps = Σ links where side ∈ metro: link.out_bps metro_capacity_bps = Σ links where side ∈ metro: link.bandwidth_bps metro_utilisation = max(in, out) / capacity
Reward history (per-epoch)
The history strip on /economics and /contributors/[code] currently estimates per-epoch payout by spreading the all-time total across distributed epochs.
avg_per_epoch_2Z = total_distributed_2Z / |distributed_epochs|
When the Foundation publishes a real per-epoch payout feed (Question #9), the chart swaps to actual per-contributor traces with no UI change.
On-chain readers
/api/onchain/topology and /api/onchain/rewards are wired and gated behind ONCHAIN_ENABLED. They return 503 with a stable shape until DZ_REGISTRY_PROGRAM_ID / DZ_REWARDS_PROGRAM_ID + the Foundation IDL are configured. Once enabled, the same endpoints return decoded account data and the malbec HTTP feeds become a fallback.
Service health
GET /api/health probes every upstream and the Rust microservice in parallel and reports overall status, per-source latency, and HTTP status codes. The sidebar pulse reads from this aggregate.
Resolved methodology questions
Confirmed by DZ Foundation 2026-05-12:
reward_percentage=Σ paid / Σ pool— raw share of cumulative payout, not pool-weighted.- Validator reward =
stake_share × validator_pool (29.25% of total fees). The full 45% pool splits 65/35 between validator operators and their clients (15.75%). Eligibility binary on publishing shreds AND not publishing retransmits. No multicast multiplier. - Public-internet capacity is infinite — only private links have bandwidth constraints.
- New-city scenarios: users supply their own assumptions (validator count, traders, etc.) via the demand editor in /simulate.
- Canonical input tables: TS port of
build_shapley_inputs.pyverified row-for-row identical on epoch 149.
Still open:
- Per-epoch, per-contributor and per-validator payout history feed (no canonical feed exists per Q9 / Q10 / Q13 — currently a guess-and-check exercise via malbec/shreds/economics).
- Forward subscription revenue feed (token, denomination, cadence, public endpoint).
- On-chain reader activation: registry + rewards program IDs and the Foundation IDL for borsh decoding (Q6).