Skip to main content

0016. Combo ids: the pmc: namespace, legs keyed by Gamma position ids, eligibility from Gamma

  • Status: Proposed
  • Date: 2026-10-01

Context

A combo is its own market with its own condition and positions, separate from the Predictions CLOB. Its legs are ordinary Predictions outcomes, but the Combos APIs name them differently from the CLOB (venue notes §16, “Legs”):
  • The RFQ keys legs by Protocol V2 position ids, Gamma’s positionIds, not by CLOB token ids (clobTokenIds) or condition ids.
  • The Data API’s leg_condition_id on combo positions is not always the Gamma conditionId (the fixtures show a 0x01… form).
  • Two sources say which markets can be legs: Gamma comboStatus on each market, and the public RFQ catalog (combos-rfq-api.polymarket.com/v1/rfq/combo-markets).
IDs that cross the venue boundary are namespaced (ADR 0004), and every signal has one source (PLAN §3.7).

Decision

  • Namespace pmc: for the Combos venue: pmc:<combo conditionId> for a combo market and pmc:<combo YES positionId> for a combo position, which is also the Accept order’s token. Legs stay pm:<tokenId>, the outcomes the rest of the app already shows, prices and links to.
  • Legs are keyed by Gamma position id only. The metadata cache stores each outcome’s positionIds entry in outcomes.position_id ('' when Gamma sends none), indexed. The browser sends leg token ids; engine/orders maps each to its position id through the catalog, and the adapter sends them as canonical decimals in ascending order. A leg the venue reports (a combo position’s legs) maps back to an outcome by position id: from the cache, or, for a closed market the cache does not hold, from Gamma by position_ids with closed=true, which is then cached. A leg is never matched by condition id or by the Data API’s leg_condition_id.
  • Eligibility comes from Gamma comboStatus only, refreshed with the rest of the market metadata into markets.combo_eligible: enabled is eligible; pending, disabled and any unknown value are not. A market with an outcome whose position id is missing is not eligible either. The public RFQ catalog is never read.
  • engine/risk refuses a quote request with a leg whose market is not eligible, or is unknown to the catalog, with combo_ineligible, and a closed market with market_closed. Two outcomes of one market, or of one neg-risk event, are combo_contradictory_legs.
  • The adapter checks that the venue’s quote echoes the request (legs by position id, side, size, wallet) and names a combo condition before the quote is shown; a mismatch is no_quote with combo_quote_mismatch.

Consequences

  • Leg prices, links and names come from the existing pm: outcomes and books; the combo itself has no book and no price but the quote (ADR 0007 and PLAN §3.7).
  • Combo positions and combo trades are told apart from Predictions ones by the pmc: prefix; Data API trade rows with is_combo are mapped to pmc: ids.
  • Eligibility is as fresh as the catalog’s Gamma refresh. A market the venue enables shows as eligible after the next refresh; one it disables in between is refused by the venue on the quote request. The app does not fill the gap from the catalog endpoint.
  • A leg the cache has never seen needs one Gamma request before a combo position can show its names.
  • Open: security finding P8-05. Gamma sends outcomes, clobTokenIds and positionIds as separate arrays, and the cache pairs them by index. The proposed fix marks a market ineligible unless its position ids are consecutive from the first, and compares the filled position’s legs with the Accept’s; the live run checks it (row 16.42).

Alternatives considered

  • Key legs by CLOB token id and let the adapter translate at the edge. Rejected: the translation still needs the position id, and storing it once in the cache keeps one mapping.
  • Match legs by condition id. Rejected: the Data API’s leg condition ids do not match Gamma’s.
  • Read the RFQ catalog for eligibility, or merge it with Gamma. Rejected: two sources for one signal; when one goes stale the merged result still looks right.
  • Reuse pm: for combos. Rejected: combo positions would look like Predictions positions to every consumer of pm: ids, including the books and the portfolio.