Polymarket Conditional Token Framework: A Technical Deep Dive
Polymarket's positions are ERC-1155 conditional tokens minted via Gnosis's CTF contract on Polygon. This deep dive explains how conditions, partitions, and the CTF Exchange contract underpin every trade, resolution, and redemption on the platform.
Every position you hold on Polymarket is an ERC-1155 token minted by the Gnosis Conditional Token Framework (CTF) contract deployed on Polygon. When you buy YES on a market, you are not just recording a database entry — you receive a real on-chain token whose value is governed entirely by cryptographic conditions and a deterministic redemption formula. Understanding the Polymarket Conditional Token Framework at this level is the prerequisite for building serious automated systems on top of it.
Conditions, Question IDs, and Outcome Collections
The CTF contract structures every binary market around a condition. A condition is created by calling prepareCondition(oracle, questionId, outcomeSlotCount) on the CTF contract (0x4D97DCd97eC945f40cF65F87097ACe5EA0476045 on Polygon). This produces a conditionId:
conditionId = keccak256(abi.encodePacked(oracle, questionId, outcomeSlotCount))
For a YES/NO market, outcomeSlotCount is 2. The oracle is an address authorized to report the result — on Polymarket, this is UMA's Optimistic Oracle. The questionId is an arbitrary bytes32 that encodes the market question, typically an IPFS hash of the question metadata.
Once a condition exists, the CTF can mint position tokens for any partition of those outcomes. A partition divides the full outcome set into non-overlapping, exhaustive subsets. For a two-outcome market the only meaningful non-trivial partitions are [YES] and [NO] — each a single-element subset. The index set is a bitmask: outcome 0 (YES) has index set 1, outcome 1 (NO) has index set 2.
Token IDs: The Math Behind the Position
The ERC-1155 token ID for a position is deterministic:
positionId = keccak256(abi.encodePacked(collateralToken, collectionId))
collectionId = keccak256(abi.encodePacked(conditionId, indexSet))
In practice, for a USDC-collateralized YES position:
collectionId = keccak256(conditionId || 0x1)(index set = 1 for outcome 0)positionId = keccak256(USDC_address || collectionId)
This is why Polymarket YES and NO tokens for the same market have specific, predictable token IDs you can derive off-chain before a market even has liquidity. Bots can and should precompute these, cache them, and use them to read balances or post approvals without hitting the API.
Splitting and Merging: How Positions Are Minted
The CTF's splitPosition function is the minting path. A user deposits collateral and receives outcome tokens:
splitPosition(collateral, parentCollectionId, conditionId, partition, amount)
For a top-level split (no parent collection), parentCollectionId is bytes32(0). Depositing 1 USDC into a YES/NO split mints exactly 1 YES token and 1 NO token — this invariant holds regardless of market price. The total value of YES + NO always equals exactly 1 USDC at redemption, which is the arbitrage constraint that powers complementary-outcome bots. Any time the sum of YES price + NO price drifts meaningfully below $1 after fees, you can buy both sides and redeem at $1. Building that trade profitably in production requires precise fee modeling and fast execution.
mergePositions is the inverse — burn equal amounts of YES and NO tokens to reclaim the underlying USDC. Bots running market-making strategies on Polymarket use this path to collapse inventory rather than selling both sides into thin books.
The CTF Exchange: CLOB Settlement on Conditional Tokens
Polymarket does not trade on the CTF contract directly. Order matching happens off-chain in a Central Limit Order Book (CLOB), and settlement uses a separate CTF Exchange contract (0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E on Polygon). This contract follows the EIP-712 order-signing standard.
An order is a signed struct containing:
maker,taker(zero address = any taker)makerAsset,takerAsset— the ERC-1155 token IDs or USDCmakerAmount,takerAmount— implied price istakerAmount / makerAmountexpiration,nonce,feeRateBps
The exchange calls matchOrders with a set of maker orders and a single taker order. It verifies signatures, computes fills, transfers tokens using safeTransferFrom on the ERC-1155 CTF contract, and handles partial fills via nonce-based accounting. There is no on-chain order book — the exchange only validates and settles. If your bot is consuming the CLOB API, you are reading a centralized matching engine that batches settlements; the API's POST /order endpoint does not guarantee immediate on-chain finality.
For latency-sensitive arbitrage bots, this architecture has a concrete implication: you can post a signed order off-chain and it becomes part of the book instantly, but on-chain settlement of the fill happens asynchronously. Your position balance in the CTF contract lags the CLOB state. Build accordingly — track CLOB fills separately from on-chain balance confirmations.
Resolution and Redemption
When a market resolves, the UMA oracle calls reportPayouts(questionId, payouts) on the CTF contract. For a YES result, payouts = [1, 0] — each YES token redeems for 1 USDC, each NO token for 0. For a NO result, payouts = [0, 1]. Partial resolutions (e.g., 50/50) are mathematically valid but Polymarket does not use them for binary markets.
The holder then calls redeemPositions(collateral, parentCollectionId, conditionId, indexSets). The contract computes:
payout = balance × payouts[i] / sum(payouts)
This call is permissionless and callable by anyone — including your bot — which means you can automate redemption for yourself or, in theory, on behalf of others. Practically, gas costs on Polygon are low enough that batching redemptions across many positions in a single transaction is worth building if you run high-frequency positions. See also the CTF adapter contract deep dive for an example of batching in Solidity.
Negative Risk and Multi-Outcome Nuances
Polymarket exposes a Negative Risk Adapter contract that bundles a set of mutually exclusive YES tokens (one per candidate in a multi-outcome market) into a single virtual position. This is implemented as a wrapper that holds CTF positions and issues its own ERC-1155 tokens — the "NegRisk" tokens you see in the CLOB for political and sports markets. The mechanics differ from a standard split: you deposit USDC into the adapter and receive one YES token per outcome; the adapter ensures exactly one pays out. The negative risk mechanics post covers this in detail, but the key operational impact is that NegRisk market orders route to a different contract address and a different set of token IDs. Bots that naively treat all Polymarket markets identically will break on NegRisk markets.
If you are building systematic strategies on Polymarket and need production-grade bot infrastructure that handles CTF mechanics, NegRisk markets, and CLOB settlement correctly, get in touch — we build and run these systems for clients.
Need a bot like this built?
We design, build and run trading bots on Solana, Hyperliquid and Polymarket.
Start a projectMore from the blog
Public vs Paid RPC: How to Run a Fair Benchmark
A fair comparison of public and paid RPC endpoints must account for quotas, workload, freshness and support guarantees.
Read articleRPC Benchmark Percentiles Explained: p50, p95 and p99
Why averages hide the latency spikes that break trading bots, wallets and production blockchain applications.
Read articleHyperliquid REST vs WebSocket Benchmark: What to Measure
A benchmark plan for Hyperliquid market data that separates request latency from streaming freshness and recovery.
Read article