Back to blog
    Engineering·January 18, 2025·12 min read·By Predexon Team

    The Hidden Complexity of Indexing Polymarket: A Developer's Deep Dive

    Why tracking wallets on the world's largest prediction market is harder than you think, and how Predexon makes it effortless.


    If you've ever tried to build analytics, portfolio tracking, or trading tools on Polymarket, you've probably discovered an uncomfortable truth: what seems like a straightforward task ("just track user trades") quickly becomes an architectural nightmare.

    In this post, we'll walk you through the real complexity of indexing Polymarket data, explain the five smart contracts and 18+ event types you need to monitor, and show you why concepts like tokenId, conditionId, and position splits make traditional approaches fall apart. Then we'll show you a better way.


    The Polymarket Stack: It's Not Just One Contract

    Most developers assume Polymarket is a single exchange contract. The reality? You need to index five separate smart contracts on Polygon, each handling different aspects of the prediction market ecosystem:

    ContractPurpose
    ConditionalTokensThe Gnosis CTF framework that handles position splits, merges, and redemptions
    CTFExchangeThe main orderbook DEX where most trading happens
    NegRiskExchangeAlternative exchange for NegRisk-style markets
    NegRiskAdapterConverts positions and prepares NegRisk markets
    FPMM (Automated Market Makers)Legacy AMM pools for liquidity provision

    Miss any one of these, and your wallet data will have gaps. A user might split collateral on ConditionalTokens, trade on CTFExchange, and redeem winnings through NegRiskAdapter, all in the same market.


    The Event Explosion: 18 Event Types You Can't Ignore

    Here's where things get interesting. Across these five contracts, you need to monitor 18 distinct event types:

    ConditionalTokens Events

    • ConditionPreparation: Market creation
    • ConditionResolution: Market settlement with payout ratios
    • PositionSplit: User splits $1 USDC into YES + NO tokens
    • PositionsMerge: User merges YES + NO back into $1 USDC
    • PayoutRedemption: User claims winnings after resolution

    Exchange Events

    • OrderFilled: The core trading event (buys and sells)
    • OrdersMatched: Price matching confirmations
    • TokenRegistered: New trading pairs

    NegRisk Events

    • PositionSplit / PositionsMerge: NegRisk variants using wrapped collateral
    • PositionsConverted: Risk profile conversions
    • PayoutRedemption: NegRisk-specific redemption (different event signature!)
    • MarketPrepared / QuestionPrepared: Market setup

    FPMM Events

    • FPMMBuy / FPMMSell: AMM trades
    • FPMMFundingAdded / FPMMFundingRemoved: Liquidity provision

    The catch? The same user action can emit events across multiple contracts. A simple "buy YES tokens" might trigger an OrderFilled on the Exchange, but a user splitting collateral directly triggers PositionSplit on ConditionalTokens. Both need to be tracked as position entries.


    The Identity Crisis: TokenId vs ConditionId vs MarketId

    Now for the part that adds real complexity: Polymarket's identifier system.

    TokenId (256-bit)

    Every position (YES or NO) has a unique tokenId, computed via Gnosis's elliptic curve math:

    tokenId = keccak256(collateral_address || keccak256(conditionId || outcomeIndex))
    • A YES position has one tokenId
    • A NO position has a different tokenId
    • The same market has two tokenIds
    • NegRisk markets use wrapped collateral, generating *different* tokenIds

    ConditionId (bytes32)

    Each market has one conditionId, computed from:

    conditionId = keccak256(oracle || questionId || outcomeSlotCount)

    Both YES and NO tokens reference the same conditionId, but you need to track them separately.

    MarketId (NegRisk only)

    NegRisk markets add another layer with marketId, which can combine multiple conditions into multi-question markets.

    The relationship looks like this:

    Market (conditionId or marketId)
      └─ Condition
          ├─ YES TokenId → UserPosition (amount, avgPrice, PnL)
          └─ NO TokenId  → UserPosition (amount, avgPrice, PnL)

    To answer a simple question like "What's this wallet's PnL?", you need to:

    1. Find all tokenIds the wallet has ever held
    2. Map each tokenId back to its conditionId
    3. Determine if the market resolved and what the payout was
    4. Calculate weighted average entry prices across splits, trades, and merges
    5. Handle partial closes, redemptions, and position conversions

    Why Splits and Merges Break Everything

    Here's the scenario that breaks naive indexing approaches:

    A user wants to buy YES tokens. They can either:

    1. Buy on the exchange: OrderFilled event, price varies
    2. Split collateral: PositionSplit event, effectively buying both YES and NO at $0.50 each

    If they split $100 USDC, they get $100 worth of YES tokens AND $100 worth of NO tokens. Their YES position should have a $0.50 cost basis, but if you're only tracking exchange trades, you'll miss it entirely.

    It gets worse with merges:

    A user holding both YES and NO can merge them back into USDC. This is equivalent to selling both positions at $0.50 each, regardless of current market prices. Your PnL calculation needs to:

    • Recognize the merge as a close event for BOTH positions
    • Price it at $0.50 (not market price)
    • Update realized PnL for both YES and NO holdings

    And redemptions add another layer:

    When a market resolves, winning positions can be redeemed for $1 USDC. Losing positions become worthless. Your indexer needs to:

    • Track the resolution event and payout ratios
    • Mark winning positions as redeemable
    • Calculate final PnL when redemption occurs
    • Handle partial redemptions

    Then there's token conversion:

    NegRisk markets introduce yet another mechanism: position conversion. Users can convert tokens between different outcomes within the same NegRisk market. For example, in a multi-outcome election market, a user might convert their "Candidate A wins" position into a "Candidate B loses" position without going through the orderbook.

    This PositionsConverted event creates a simultaneous close on one position and open on another, with pricing determined by the market's internal conversion logic. Your indexer needs to:

    • Track the conversion as a close event for the source position
    • Track it as an open event for the destination position
    • Calculate the effective conversion price
    • Maintain accurate cost basis for both positions
    • Handle the wrapped collateral mechanics unique to NegRisk

    Miss this, and users who convert positions will have phantom gains or losses in your system.


    The Internal Operations Trap

    Here's a subtle issue to watch out for: internal contract operations.

    When you trade on the CTFExchange, the exchange contract internally calls ConditionalTokens to transfer positions. This emits PositionSplit or PositionsMerge events with the Exchange as the stakeholder, not the user.

    If you naively index all ConditionalTokens events, you'll double-count every trade. You need to filter out events where:

    • The stakeholder is the Exchange contract
    • The stakeholder is the NegRiskAdapter contract

    But you CAN'T filter all of these, because users can also interact with ConditionalTokens directly. You need to distinguish user-initiated splits from exchange-internal operations.


    The Price Calculation Nightmare

    Different events have different pricing:

    Event TypePrice Logic
    Exchange OrderFilledmakerAmountFilled / outcomeTokensBought (variable)
    PositionSplitFixed $0.50 per outcome
    PositionsMergeFixed $0.50 per outcome
    FPMM Buy/SellAMM curve pricing (variable)
    PayoutRedemption$1 for winners, $0 for losers

    Your cost basis calculation needs weighted averages across all these sources:

    avgPrice = Σ(price_i × quantity_i) / Σ(quantity_i)

    And when users partially close positions, you need FIFO, LIFO, or average cost accounting, consistently applied across splits, trades, and merges.


    What You Actually Need to Build

    To properly track Polymarket wallets, you need:

    1. Multi-contract event ingestion: 5 contracts, 18 event types
    2. Identity resolution: Map tokenIds to conditionIds to markets
    3. Internal operation filtering: Skip exchange-internal events
    4. Multi-source price tracking: Different logic per event type
    5. Position lifecycle management: Opens, partial closes, full closes, redemptions
    6. PnL calculation engine: Realized, unrealized, per-position, per-wallet
    7. Historical state reconstruction: What was the position at any point in time?
    8. Real-time updates: Up to 6 seconds faster than native Polymarket streams

    This is months of engineering work. And we haven't even mentioned:

    • Handling chain reorgs
    • Managing RPC rate limits
    • Storing and querying terabytes of historical data
    • Computing rolling metrics (7-day PnL, 30-day volume, etc.)
    • Classifying trading styles and patterns

    The Predexon Solution

    We built Predexon because we faced this exact problem and solved it.

    Our indexer handles all the complexity:

    • Monitors all contracts in real-time
    • Processes all event types with correct pricing logic
    • Resolves tokenId/conditionId relationships automatically
    • Filters internal operations while preserving user-initiated events
    • Calculates accurate cost basis across splits, trades, and merges
    • Tracks realized and unrealized PnL per position and per wallet
    • Maintains rolling metrics (1-day, 7-day, 30-day windows)
    • Stores historical snapshots for any point-in-time queries

    What you get through our API:

    With a single API call, you can retrieve comprehensive wallet insights including:

    • Aggregate PnL: Total realized and unrealized profit/loss across all positions
    • Performance metrics: Win rate, profit factor, ROI, and trading volume
    • Position details: Every open position with entry price, current price, size, and individual PnL
    • Trading behavior: Style classification, average hold times, trade frequency patterns
    • Rolling windows: Pre-computed 1-day, 7-day, and 30-day metrics for trends
    • Historical snapshots: Point-in-time position states for any date
    • Market-level breakdowns: Performance segmented by individual markets

    One API call. No contract ABIs. No event parsing. No identifier mapping. No price calculation edge cases.


    Use Cases We Enable

    With accurate, real-time wallet data, you can build:

    • Portfolio trackers: Show users their complete Polymarket positions and PnL
    • Leaderboards: Rank traders by returns, win rate, or volume
    • Copy trading: Follow successful predictors
    • Risk analytics: Monitor position concentration and exposure
    • Trading bots: React to wallet movements in real-time
    • Research tools: Analyze market participant behavior

    Get Started

    We've done the dirty work so you don't have to. Our API gives you:

    • Complete wallet insights in milliseconds
    • Historical data going back to Polymarket's launch
    • Rolling metrics pre-computed for common time windows
    • WebSocket feeds for real-time position updates

    Stop wrestling with contract ABIs and event parsing. Start building the features your users actually want.

    Ready to get started?

    Get an API key and start querying prediction market data in minutes — free tier, no credit card.