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:
| Contract | Purpose |
|---|---|
| ConditionalTokens | The Gnosis CTF framework that handles position splits, merges, and redemptions |
| CTFExchange | The main orderbook DEX where most trading happens |
| NegRiskExchange | Alternative exchange for NegRisk-style markets |
| NegRiskAdapter | Converts 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 creationConditionResolution: Market settlement with payout ratiosPositionSplit: User splits $1 USDC into YES + NO tokensPositionsMerge: User merges YES + NO back into $1 USDCPayoutRedemption: User claims winnings after resolution
Exchange Events
OrderFilled: The core trading event (buys and sells)OrdersMatched: Price matching confirmationsTokenRegistered: New trading pairs
NegRisk Events
PositionSplit/PositionsMerge: NegRisk variants using wrapped collateralPositionsConverted: Risk profile conversionsPayoutRedemption: NegRisk-specific redemption (different event signature!)MarketPrepared/QuestionPrepared: Market setup
FPMM Events
FPMMBuy/FPMMSell: AMM tradesFPMMFundingAdded/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:
- Find all tokenIds the wallet has ever held
- Map each tokenId back to its conditionId
- Determine if the market resolved and what the payout was
- Calculate weighted average entry prices across splits, trades, and merges
- 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:
- Buy on the exchange:
OrderFilledevent, price varies - Split collateral:
PositionSplitevent, 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 Type | Price Logic |
|---|---|
| Exchange OrderFilled | makerAmountFilled / outcomeTokensBought (variable) |
| PositionSplit | Fixed $0.50 per outcome |
| PositionsMerge | Fixed $0.50 per outcome |
| FPMM Buy/Sell | AMM 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:
- Multi-contract event ingestion: 5 contracts, 18 event types
- Identity resolution: Map tokenIds to conditionIds to markets
- Internal operation filtering: Skip exchange-internal events
- Multi-source price tracking: Different logic per event type
- Position lifecycle management: Opens, partial closes, full closes, redemptions
- PnL calculation engine: Realized, unrealized, per-position, per-wallet
- Historical state reconstruction: What was the position at any point in time?
- 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.