Market Normalization
Each platform's read-proxy normalizes the venue's raw upstream response into a platform-specific
TypeScript interface — DFlowMarket, PolyMarket, LimitlessMarket, PredictNormalizedMarket,
RainMarket, OpinionMarket, OvertimeMarket. These interfaces are defined in each
services/<platform>/markets.ts (and several are mirrored on the web side in
lib/<platform>Trade.ts). There is no shared base type — magma-sdk's src/types.ts is
generic and defines no cross-platform market shape.
Why there is no single unified type
The eight venues are genuinely different products and a forced "union" type would lose the fields each one needs to trade:
- Identifier differs. Polymarket keys on
conditionId; Limitless onslug/address/conditionId; Kalshi/DFlow onticker(+eventTicker); Predict onid; Overtime ongameId; Rain/Opinion onid. - Outcome model differs. Binary YES/NO token ids (Polymarket
clobTokenIds, PredictyesTokenId/noTokenId, DFlowaccounts.*.yesMint/noMint) vs sports odds arrays (Overtimeodds[], lines, player props, combined positions) vs AMM yes/no prices (Rain) vs macro thresholds (OpinioneconomicIndicator/threshold/condition). - Pricing differs. Some return
yesPrice/noPrice0–1; Polymarket returns stringifiedoutcomePrices; Limitless can return percentages 0–100; Kalshi returns bid/ask strings; Overtime returns American/decimal/implied odds. - Trade fields differ. Neg-risk and yield-bearing families (Predict), CLOB venue/adapter (Limitless), settlement mint (DFlow), merkle proof (Overtime).
So each platform keeps its own interface, and the client composes them per-tab. The one thing
the backend does standardize across platforms is a derived MAGMA category
(SPORTS, POLITICAL, ONCH, ECONOMICS, GEOPOLITICAL, …) so the /markets grid can filter
and color uniformly.
The per-platform <Platform>Market interfaces
The defining fields of each (see the source for the full shape):
| Interface (file) | Primary id | Outcome / price fields | Trade-relevant extras |
|---|---|---|---|
DFlowMarket (dflow/markets.ts) | ticker, eventTicker | yesBid/yesAsk/noBid/noAsk (strings) | accounts (per settlement mint: yesMint, noMint, settlementMint, marketLedger), status, result |
PolyMarket (polymarket/markets.ts) | conditionId, slug | outcomes[], outcomePrices[], yesTokenId, noTokenId | enableOrderBook, closed, resolved, liquidity |
LimitlessMarket (limitless/markets.ts) | id, address, conditionId, slug | yesPrice, noPrice (rescaled from 0–100) | tradeType, marketType, tradable, collateralDecimals |
PredictNormalizedMarket (predict/markets.ts) | id, slug | yesPrice, noPrice, outcomes[] (indexSet, tokenId, bid/ask) | feeRateBps, isYieldBearing, isNegRisk, conditionId, source:'predict', chain:'bnb' |
RainMarket (rain/markets.ts) | id | yesPrice, noPrice | chain, liquidityPool, status, resolution, isPrivate |
OpinionMarket (opinion/markets.ts) | id | yesPrice, noPrice | economicIndicator, threshold, condition, aiOracleConfidence, dataSources[] |
OvertimeMarket (overtime/markets.ts) | gameId | odds[] (american/decimal/impl) | leagueId, typeId, line, maturity, proof[], playerProps, childMarkets[] |
PredictNormalizedMarketPredict's normalized market carries its own source/chain tags, per-outcome CTF indexSet
(1 = YES, 2 = NO) and tokenId, derived yesPrice/noPrice from best bid/ask, plus the
isNegRisk / isYieldBearing flags the SDK needs to pick the right exchange at order-build time.
None of those fields are meaningful for Overtime's sports markets — which is exactly why the types
stay separate.
Common verbs
Despite the different shapes, the services expose a consistent verb set (names vary slightly; the route shape is uniform). Not every platform implements every verb:
| Verb | Purpose | Route shape | Platforms |
|---|---|---|---|
| get list | normalized active markets (paged, category filter, cached) | GET /v1/<p>/markets | all 8 |
| get one | single market detail by the platform's id | GET /v1/<p>/markets/:id | all (:ticker, :conditionId, :slug, :gameId, :id) |
| search | keyword search | GET /v1/<p>/markets/search?q= | Polymarket, Limitless, Predict*, Rain, Opinion, Overtime |
| orderbook | CLOB depth | GET /v1/<p>/markets/:id/orderbook | DFlow, Predict, Opinion |
| positions | a wallet's holdings | GET /v1/<p>/positions/:wallet | all 8 |
| quote | live price/payout for a size | POST /v1/<p>/trade/quote · POST /v1/<p>/quote | Rain, Overtime (DFlow via build) |
* Predict exposes search via its market list/detail; the dedicated search route is present on
Polymarket/Limitless/Rain/Opinion/Overtime. See API Reference
for the exact path per platform.
Positions normalization
positions is the verb that most often has no direct upstream endpoint, so the service
assembles it:
- DFlow / Kalshi has no positions API — the service reads the wallet's on-chain
Token-2022 accounts (
getTokenAccountsByOwner) and joins each mint to market metadata (accounts.yesMint/noMint) to produce side, mark price, value,won,resolved, andredeemable. - Polymarket reads the Data API
/positions?user=. - Limitless / Predict / Rain / Opinion / Overtime read each venue's portfolio/positions feed
and return it under a
positionsarray.
How the client composes them
The /markets page (magma-web/src/app/(auth)/markets/page.tsx) keeps per-platform state and
fetches each platform with its own typed lib function (fetchRainMarkets, fetchOpinionMarkets,
fetchOvertimeMarkets, the Kalshi/Polymarket/Limitless/Predict fetchers, etc.). It then:
- renders all venues into one categorized, searchable grid alongside MAGMA-native binary markets, keyed by category color;
- routes each card to its own trade modal (
KalshiTradeModal,PolymarketTradeModal,LimitlessTradeModal,PredictTradeModal,RainTradeModal,OpinionTradeModal,OvertimeBetModal), each typed to its platform's<Platform>Market; and - resolves the market's chain (
VENUE_CHAIN_KEY/useChainControllerfor EVM,solana:mainnetfor Kalshi) and switches the wallet before the modal's lib signs.
The composition is client-side and per-platform — the unification is presentational, not a shared data model.
See also
- Platform Integrations — each interface's source files.
- Trading & Settlement — what the trade fields drive.
- API Reference — the exact read endpoints per platform.