·
Polymarket Contract Architecture
A TL;DR tour of Polymarket's 13-contract architecture on Polygon: pUSD as a quiet abstraction over the USDC.e → native USDC migration, v1 vs v2 structural differences, the NegRisk invariant enforced at oracle time, convertPositions capital efficiency, and where the real immutability lives.
Polymarket is an “off-chain order book + on-chain settlement” prediction market — 13 production contracts on Polygon, with three independently evolvable layers built on top of Gnosis ConditionalTokens (an immutable ERC-1155 ledger).
The shape, in one diagram
USER LAYER: pUSD (Polymarket's own stablecoin, abstracts USDC.e / native USDC)
│
MATCHING: CTFExchange × 4 (binary v1/v2, neg-risk v1/v2)
│
WRAPPING: CtfCollateralAdapter × 2 (pUSD ⟷ USDC.e or WCOL)
│
CAPITAL EFF: NegRiskAdapter + WCOL (convertPositions for N-of-N markets)
│
LEDGER: Gnosis ConditionalTokens (immutable, trust anchor)
▲
ORACLE: UmaCtfAdapter × 3 → UMA Optimistic Oracle V2
What’s interesting
1. pUSD is a quiet abstraction win. Polygon’s USDC.e (bridged) → native USDC migration would have been a nightmare to expose to users. Polymarket put their own ERC-20 in front of it — two role-gated mints (MINTER_ROLE for mint/burn, WRAPPER_ROLE for wrap/unwrap) backed by a VAULT reserve address. ~$376M circulating today. v1 path doesn’t have it — users there still hold USDC.e directly.
2. v1 vs v2 is structural, not parametric. Binary v2 and neg-risk v2 share the same compiled artifact (CTFExchange) with different constructor params. v1 has two separate contracts (CTFExchange and NegRiskCtfExchange), compiler v0.8.15 vs v0.8.34, and crucially: in v1 neg-risk getCtf() returns NegRiskAdapter — the matching hot path hops through the adapter. In v2 it returns the real CTF directly. NegRiskAdapter only appears on split/merge/convert/redeem.
3. The “Negative Risk” invariant is enforced at oracle time. In a market with N candidate questions, the second attempt to report YES reverts. NO can be reported repeatedly. This single line — if (data.determined()) revert MarketAlreadyDetermined() — is the entire constraint that makes convertPositions capital-efficient.
4. convertPositions is a Polymarket-unique on-chain primitive. Hand over your NO tokens for K of N questions, get back (K-1) × amount of collateral + YES tokens for the remaining (N-K). Mathematically conservation-of-value (only 1 YES can win), so extracting collateral from K mutually exclusive NOs is arbitrage-free. Doesn’t exist in traditional CTF docs.
5. WCOL has asymmetric permissions. Anyone can unwrap WCOL → USDC.e. But wrap, mint, release, burn are all onlyOwner (NegRiskAdapter only). This asymmetry — owner-only mint/release — is the physical basis for capital efficiency.
6. The real immutability is in 3 places. Everything else can be bypassed by new versions, but:
- CTF itself (Gnosis-deployed, nobody can upgrade)
-
EMERGENCY_SAFETY_PERIOD = 172800 seconds(2 days, hard-coded in UmaCtfAdapter) - NegRisk outcome rule (≤1 YES per market, hard-coded in
_reportOutcome)
Old versions never retire — they decay naturally as old markets settle.
Trust topology (TL;DR)
- Matching: centralized (Polymarket operator)
- Custody: decentralized (smart-contract proxy wallets, EIP-1271 + CREATE2)
- Ledger: fully decentralized (Gnosis CTF, no admin)
- Oracle: delegated to UMA’s independent community
- User layer / governance: Polymarket multisig
Why this matters for AI agents
If you query Polymarket on-chain, you need to know:
- NegRisk-side positionIds are derived from WCOL, not USDC.e
- User positions live in the proxy wallet, not the EOA —
getProxyWalletAddress(eoa)first, then query EOA as fallback - v1 has no
getCtfCollateral— querying collateral info must branch on version -
questions(questionID)on UmaCtfAdapter is the authoritative source for “is this market real or flagged”
Full architecture doc with addresses, ABI tables, and all the references → smarts.md
Every piece of on-chain data was pulled live via Smarts — it turns any verified EVM contract into queryable live docs.
转发此帖子?
与您的关注者分享。
回复