Skip to main content
DZ CONTRIBUTOR

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.

SourceUse
malbeclabs/topologyDevices, links, validators, metros (live)
malbeclabs/statusLink health, active issues, top-utilised paths
malbeclabs/statsValidators-on-DZ count, stake share, total bandwidth
dz/economic-hubAll-time 2Z distributed, debt, burn, per-contributor reward %
DZ contributor-rewards snapshotsPer-epoch S3 snapshots used by the simulator
DZ fees CSVHistorical Solana epoch fee totals (859–938)
Jupiter price APILive 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.py verified 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).