---
name: msr-market-reader
description: Read and explain a ChanceDB Metalog forecast from its Base market contract, including the current view, a submitted revision, or the crowd average.
---

# Read a ChanceDB market

Use this skill when a user provides a ChanceDB market address and asks an agent to inspect or explain its forecast.

## Query the read-only API

When the deployed operator app is reachable, prefer its stateless endpoints for machine-readable values:

- `GET https://market.chancedb.com/api/quantile?market=0x…&p=0.9[&view=current|crowd|<reportId>]` returns the Metalog quantile in fractional market units (`0.01` means `1%`).
- `GET https://market.chancedb.com/api/probability?market=0x…&x=0.05[&view=current|crowd|<reportId>]` returns `cdf`, `exceedance`, and `pdf`.
- `GET https://market.chancedb.com/api/outcome?market=0x…` returns the resolved outcome and resolution timestamp; `GET https://market.chancedb.com/api/leaderboard?market=0x…` ranks unchallenged submissions by log-density at that outcome. Both return 409 before resolution.
- Omitted `view` defaults to `current` before resolution and `crowd` after resolution (falling back to `current` when no unchallenged reports exist). Explicit `current` remains available; `crowd` is the coefficient-wise mean of non-slashed reports, with shorter arrays zero-padded; a numeric value selects that report's `newA`.
- Curve responses include decimal `coefficients`, `blockNumber`, and `blockTimestamp`; outcome and leaderboard responses include the resolution time and block provenance. These are read-only requests and do not store state.
- Invalid input returns 400; an upstream Base RPC failure returns 502. The operator server gets its Base RPC URL from the runtime `BASE_RPC_URL` environment variable, configured here for Dwellir's Base mainnet archive endpoint.

## Read contract data

- If the API is unavailable, read the market on Base (chain ID `8453`) directly. The shared operator ABI is in `apps/operator/lib/market-abi.ts`.
- `currentA()` returns the live market's Metalog coefficients as `int256[]` in WAD units. Convert each coefficient to ordinary decimal units by dividing by `1e18`.
- `reportCount()` returns the number of submitted revisions. `reportA(id)` returns `(reporter, prevA, newA, reportedAt, scaleWad, escrowUsdc, slashed, claimed)`; use `newA` for that submission and ignore it in the crowd average when `slashed` is true. `scale()` is dynamic and may ratchet upward while funding remains open.
- `escrowUsdc` is the report-time deposit in USDC base units. Do not read or expect an `escrowPerReport()` view.
- To form the equal-weight Crowd View, zero-pad shorter coefficient arrays and average each coefficient position across non-slashed reports. This is the coefficient-wise mean quantile function; do not average PDFs or refit a Metalog.
- Coefficients describe an unbounded Metalog quantile in fractional BTC/USD return units (`0.01` means `1%`). The market's `domainLo` and `domainHi` are market/scoring limits, not Metalog lower and upper bounds; do not encode them as SIPmath distribution bounds.

When summarizing a view, identify whether it came from `currentA()`, a report's `newA`, or the coefficient-wise Crowd View. Keep the response read-only unless the user separately requests an on-chain action.

## Forecaster rewards

Submitting a forecast (`report(int256[] a)`) stakes the report's `escrowUsdc` and replaces the head curve. Two reward legs:

- **USDC, paid at claim.** At resolution each report scores an improvement in nats: `ln(flooredDensity(newA, outcome)) − ln(flooredDensity(prevA, outcome))` — the log-score gain over the exact curve it replaced, so copying the head earns exactly 0. Payout is `max(0, escrow + scale × improvement)`; improvement can be negative, but the loss is capped at the escrow. Claims are permissionless (`claim(id)` before the market's claim deadline; after it, `claimLate(id)` returns principal only), and the keeper bot settles them for forecasters.
- **SBB, the primary leg.** Whatever a market's funded pot is not won by forecasters sweeps after its claim deadline to Base:13 ("Split's Bankr Bot", token `0x761A27084Efd69b94889F699D7A7493EC42f9622`) and returns as SBB. Per swept market, the realized SBB splits 70% pro-rata by positive improvement, 20% equally across unique reporters whose curve genuinely moved the head (floored-density ln-ratio range ≥ 0.1 nats — duplicate curves earn 0), 10% reserve. Distributions are batched weekly from the sweep beneficiary.
- **Standings.** `GET https://market.chancedb.com/api/forecasters` returns global standings (USDC profit, positive and total nats, win rate, streaks, undistributed SBB pool). The human board is https://market.chancedb.com/forecasters.

Expected value, briefly: stake ≈ `scale × scoreRange`, and one nat is worth `scale` USDC, so at small pots the USDC leg alone rarely covers Base gas — the SBB leg is where the return concentrates (at recent value anchors, roughly 25–50× the USDC leg per nat). Improvement is measured against the curve a report replaces, so the first genuine view after the seed usually has the most headroom.

Safety: a submitted curve must keep `mPrime ≥ slopeFloor` across the domain; an invalid curve can be challenged and slashed inside the 1-hour window after it becomes the head. Fit with margin — keep the fitted minimum slope at least 2× the market's `slopeFloor`.
