From 3e4f75f4e4845d79f56d2a2f313b8b97f0c5ead3 Mon Sep 17 00:00:00 2001 From: Sayo <82053242+wtfsayo@users.noreply.github.com> Date: Sat, 18 Apr 2026 12:06:15 +0530 Subject: [PATCH 1/2] docs: migrate polymarket skill docs to CLOB v2 --- README.md | 170 ++++++++++++++----------- SKILL.md | 152 ++++++++++++----------- authentication.md | 223 +++++++++++++++++---------------- bridge.md | 100 ++++++--------- ctf-operations.md | 163 +++++++++--------------- gasless.md | 229 ++++++++++++++-------------------- market-data.md | 259 +++++++++++++++++++------------------- order-patterns.md | 307 ++++++++++++++++------------------------------ 8 files changed, 720 insertions(+), 883 deletions(-) diff --git a/README.md b/README.md index bd2f5a8..80887bb 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,108 @@ # Polymarket Integration Skill -Agent skill for building on Polymarket — the world's largest prediction market. Gives agents the knowledge to authenticate, place orders, read markets, stream real-time data, manage positions, bridge assets across chains, and execute gasless transactions. +Agent skill for building on Polymarket CLOB V2. Covers authentication, trading, market data, WebSockets, CTF token operations, bridge flows, and gasless relayer usage. ## What's Included ``` web3-polymarket/ -├── SKILL.md # Entry point — quick reference, client setup, core patterns -├── README.md # This file -├── authentication.md # L1/L2 auth, builder headers, credential lifecycle -├── order-patterns.md # Order types, tick sizes, cancel, heartbeat, errors -├── market-data.md # Gamma API, Data API, CLOB orderbook, subgraph -├── websocket.md # Market/user/sports channels, subscribe, heartbeat -├── ctf-operations.md # Split, merge, redeem, negative risk, token IDs -├── bridge.md # Deposits, withdrawals, supported chains/tokens -└── gasless.md # Relayer client, wallet deployment, builder setup +├── SKILL.md +├── README.md +├── authentication.md +├── order-patterns.md +├── market-data.md +├── websocket.md +├── ctf-operations.md +├── bridge.md +└── gasless.md ``` -## How It Works - -The skill uses **progressive disclosure** to stay efficient with context: +## V2 status -1. **SKILL.md loads first** — contains API endpoints, contract addresses, client setup, and core code patterns. Enough for most tasks. -2. **Reference files load on demand** — when a task needs deeper detail (e.g., full error code list, bridge chain support, WebSocket event schemas), the agent reads the relevant file. +Validated against: +- `https://docs.polymarket.com/llms.txt` +- `https://docs.polymarket.com/v2-migration.md` -This keeps the initial context small (~200 lines) while giving access to ~1,700 lines of detailed reference material when needed. +Key V2 changes reflected here: +- `@polymarket/clob-client-v2` / `py-clob-client-v2` +- constructor uses an **options object** in TypeScript and `chain` instead of `chain_id` +- signed orders use `timestamp`, `metadata`, `builder`; not `nonce`, `feeRateBps`, `taker` +- **pUSD** replaces USDC.e as trading collateral +- builder attribution uses **`builderCode`**, not `POLY_BUILDER_*` headers +- exchange contracts moved to V2 addresses +- Gamma keyset pagination endpoints are documented -## When Agents Use This Skill +## How It Works -An agent activates this skill when a user asks about: +1. **SKILL.md loads first** for quick-reference setup and core flows. +2. **Reference files load on demand** for deeper detail. -- **Authentication** — API keys, EIP-712 signing, HMAC-SHA256, builder credentials -- **Trading** — placing limit/market orders (GTC, GTD, FOK, FAK), batch orders, cancellation, heartbeat keepalive -- **Market data** — fetching events/markets from Gamma API, reading orderbook prices/spreads/midpoints, price history -- **Real-time data** — WebSocket subscriptions for orderbook updates, trade notifications, sports scores -- **Token operations** — splitting USDC.e into Yes/No tokens, merging, redeeming after resolution -- **Bridging** — depositing from 15+ chains, withdrawing, checking status -- **Gasless transactions** — relayer client for gas-free onchain operations -- **Negative risk** — multi-outcome markets, token conversion, augmented neg risk +## When Agents Use This Skill -## Quick Start for Humans +Use it for: +- authentication and API key setup +- V2 order creation and order management +- market/event discovery and orderbook reads +- WebSocket streaming +- pUSD / CTF operations +- bridging and funding flows +- gasless relayer transactions +- builder code attribution -If you're a developer reading this directly (not an agent), here's the fastest path: +## Quick Start -### 1. Install the SDK +### Install the V2 SDK ```bash # TypeScript -npm install @polymarket/clob-client ethers@5.8.0 +npm install @polymarket/clob-client-v2 ethers@5.8.0 # Python -pip install py-clob-client +pip install py-clob-client-v2 ``` -### 2. Get API Credentials +### Create API credentials ```typescript -import { ClobClient } from "@polymarket/clob-client"; +import { ClobClient } from "@polymarket/clob-client-v2"; import { Wallet } from "ethers"; -const client = new ClobClient( - "https://clob.polymarket.com", - 137, - new Wallet(process.env.PRIVATE_KEY) -); +const signer = new Wallet(process.env.PRIVATE_KEY!); +const client = new ClobClient({ + host: "https://clob.polymarket.com", + chain: 137, + signer, +}); + const creds = await client.createOrDeriveApiKey(); ``` -### 3. Place an Order +### Place an order ```typescript -const tradingClient = new ClobClient( - "https://clob.polymarket.com", - 137, +import { ClobClient, Side, OrderType } from "@polymarket/clob-client-v2"; + +const tradingClient = new ClobClient({ + host: "https://clob.polymarket.com", + chain: 137, signer, creds, - 2, // GNOSIS_SAFE (most common) - "FUNDER_ADDR" // from polymarket.com/settings -); + signatureType: 2, + funderAddress: process.env.FUNDER_ADDRESS!, + builderConfig: process.env.POLY_BUILDER_CODE + ? { builderCode: process.env.POLY_BUILDER_CODE } + : undefined, +}); const response = await tradingClient.createAndPostOrder( - { tokenID: "TOKEN_ID", price: 0.50, size: 10, side: "BUY" }, + { + tokenID: "TOKEN_ID", + price: 0.50, + size: 10, + side: Side.BUY, + }, { tickSize: "0.01", negRisk: false }, - "GTC" + OrderType.GTC, ); ``` @@ -90,23 +110,25 @@ const response = await tradingClient.createAndPostOrder( | Concept | Description | |---------|-------------| -| **USDC.e** | Bridged USDC on Polygon — the collateral token for all markets | -| **Condition ID** | Identifies a market (used in API as `market` or `conditionID`) | -| **Token ID** | Identifies a specific outcome token (Yes or No) within a market | -| **Funder** | The proxy wallet address that holds funds — find at polymarket.com/settings | -| **Signature Type** | `0` = EOA, `1` = POLY_PROXY (Magic Link), `2` = GNOSIS_SAFE (most common) | -| **Neg Risk** | Multi-outcome markets where outcomes are linked — set `negRisk: true` in order options | -| **Tick Size** | Minimum price increment for a market — must match or orders are rejected | +| **pUSD** | The collateral token used for trading on Polymarket | +| **USDC.e** | Source asset wrapped into pUSD for API-only flows | +| **Condition ID** | Market identifier | +| **Token ID** | Outcome token identifier | +| **Funder** | Wallet address holding funds | +| **Signature Type** | `0` EOA, `1` POLY_PROXY, `2` GNOSIS_SAFE | +| **Builder Code** | Public builder identifier attached to orders for attribution | +| **Neg Risk** | Multi-outcome markets with linked outcomes | ## API Endpoints | API | Base URL | Auth Required | |-----|----------|---------------| -| CLOB | `https://clob.polymarket.com` | L2 headers for trades, none for reads | +| CLOB V2 | `https://clob.polymarket.com` | L2 for trades, none for reads | +| CLOB V2 Test | `https://clob-v2.polymarket.com` | L2 for trades, none for reads | | Gamma | `https://gamma-api.polymarket.com` | None | | Data | `https://data-api.polymarket.com` | None | | Bridge | `https://bridge.polymarket.com` | None | -| Relayer | `https://relayer-v2.polymarket.com/` | Builder headers | +| Relayer | `https://relayer-v2.polymarket.com/` | Relayer API key or Builder API key | | WS Market | `wss://ws-subscriptions-clob.polymarket.com/ws/market` | None | | WS User | `wss://ws-subscriptions-clob.polymarket.com/ws/user` | API creds in message | | WS Sports | `wss://sports-api.polymarket.com/ws` | None | @@ -115,31 +137,31 @@ const response = await tradingClient.createAndPostOrder( | Contract | Address | |----------|---------| -| USDC.e (Bridged USDC) | `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` | +| pUSD | `0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB` | +| USDC.e | `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` | | CTF | `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045` | -| CTF Exchange | `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` | -| Neg Risk CTF Exchange | `0xC5d563A36AE78145C45a50134d48A1215220f80a` | +| CTF Exchange V2 | `0xE111180000d2663C0091e4f400237545B87B996B` | +| Neg Risk CTF Exchange V2 | `0xe2222d279d744050d28e00520010520000310F59` | | Neg Risk Adapter | `0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296` | +| Collateral Onramp | `0x93070a847efEf7F70739046A929D47a521F5B8ee` | +| Collateral Offramp | `0x2957922Eb93258b93368531d39fAcCA3B4dC5854` | ## File Guide -| File | Read when you need to... | -|------|--------------------------| -| [SKILL.md](SKILL.md) | Get started — has everything for basic integration | -| [authentication.md](authentication.md) | Understand L1/L2 auth flow, builder headers, or troubleshoot credential issues | -| [order-patterns.md](order-patterns.md) | Use advanced order types (GTD, post-only, batch), handle errors, or implement heartbeat | -| [market-data.md](market-data.md) | Query markets by slug/tag, paginate results, use subgraph, or estimate fill prices | -| [websocket.md](websocket.md) | Stream real-time orderbook updates, trade notifications, or sports scores | -| [ctf-operations.md](ctf-operations.md) | Split/merge/redeem tokens, work with neg risk markets, or compute token IDs | -| [bridge.md](bridge.md) | Deposit from other chains, withdraw, check supported assets, or track transaction status | -| [gasless.md](gasless.md) | Set up gas-free transactions via the relayer, deploy wallets, or configure builder credentials | +| File | Use it when you need to... | +|------|----------------------------| +| [SKILL.md](SKILL.md) | Get the quick-reference V2 setup | +| [authentication.md](authentication.md) | Understand L1/L2 auth and builderCode attribution | +| [order-patterns.md](order-patterns.md) | Create, submit, cancel, and maintain orders | +| [market-data.md](market-data.md) | Query events, markets, CLOB data, and pagination | +| [websocket.md](websocket.md) | Stream market and user updates | +| [ctf-operations.md](ctf-operations.md) | Split, merge, and redeem pUSD-backed positions | +| [bridge.md](bridge.md) | Deposit, withdraw, and track bridge flows | +| [gasless.md](gasless.md) | Use the relayer for gasless onchain actions | ## SDKs -- **TypeScript**: [@polymarket/clob-client](https://github.com/Polymarket/clob-client) -- **Python**: [py-clob-client](https://github.com/Polymarket/py-clob-client) -- **Rust**: [rs-clob-client](https://github.com/Polymarket/rs-clob-client) +- **TypeScript CLOB V2**: [@polymarket/clob-client-v2](https://www.npmjs.com/package/@polymarket/clob-client-v2) +- **Python CLOB V2**: [py-clob-client-v2](https://pypi.org/project/py-clob-client-v2/) - **Builder Relayer (TS)**: [@polymarket/builder-relayer-client](https://github.com/Polymarket/builder-relayer-client) - **Builder Relayer (Python)**: [py-builder-relayer-client](https://github.com/Polymarket/py-builder-relayer-client) -- **Builder Signing (TS)**: [@polymarket/builder-signing-sdk](https://github.com/Polymarket/builder-signing-sdk) -- **Builder Signing (Python)**: [py-builder-signing-sdk](https://github.com/Polymarket/py-builder-signing-sdk) diff --git a/SKILL.md b/SKILL.md index 171cbfb..94c56a8 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,7 +1,7 @@ --- name: web3-polymarket -description: Polymarket integration for prediction market trading on Polygon. Covers authentication (L1 EIP-712, L2 HMAC-SHA256, builder headers), order placement (GTC/GTD/FOK/FAK, batch, post-only, heartbeat), market data (Gamma API, Data API, orderbook, subgraph), WebSocket streaming (market/user/sports channels), CTF operations (split, merge, redeem, negative risk), bridge (deposits, withdrawals, multi-chain), and gasless relayer transactions. Use when building AI agents, autonomous market makers, prediction market UIs, or any application integrating with Polymarket on Polygon. -compatibility: Requires network access to Polymarket APIs (clob.polymarket.com, gamma-api.polymarket.com) and Polygon RPC +description: Polymarket integration for prediction market trading on Polygon. Covers V2 CLOB authentication (L1 EIP-712, L2 HMAC-SHA256), order placement (GTC/GTD/FOK/FAK, batch, post-only, heartbeat), market data (Gamma API, Data API, orderbook, keyset pagination), WebSocket streaming (market/user/sports channels), CTF operations (split, merge, redeem, negative risk), bridge flows, and gasless relayer transactions. Use when building AI agents, autonomous market makers, prediction market UIs, or any application integrating with Polymarket on Polygon. +compatibility: Requires network access to Polymarket APIs (clob.polymarket.com, gamma-api.polymarket.com, relayer-v2.polymarket.com) and Polygon RPC --- # Polymarket Skill @@ -10,64 +10,79 @@ compatibility: Requires network access to Polymarket APIs (clob.polymarket.com, Use this skill when the user asks about or needs to build: - Polymarket API authentication (L1/L2, API keys, HMAC signing) -- Placing or managing orders (limit, market, GTC, GTD, FOK, FAK, batch, cancel) -- Reading orderbook data (prices, spreads, midpoints, depth) -- Market data fetching (events, markets, by slug, by tag, pagination) +- Placing or managing orders on **CLOB V2** +- Reading orderbook data (prices, spreads, midpoints, CLOB market info) +- Market data fetching (events, markets, by slug, by tag, keyset pagination) - WebSocket subscriptions (market channel, user channel, sports) - CTF operations (split, merge, redeem positions) - Negative risk markets (multi-outcome, conversion, augmented neg risk) -- Bridge operations (deposits, withdrawals, multi-chain) -- Gasless transactions (relayer client, order attribution) -- Builder program integration (order attribution, API keys, tiers) -- Polymarket SDK usage (TypeScript @polymarket/clob-client, Python py-clob-client) +- Bridge operations (deposits, withdrawals, supported assets, status) +- Gasless transactions (relayer client, wallet deployment) +- Builder program integration (`builderCode` attribution) +- Polymarket SDK usage (TypeScript `@polymarket/clob-client-v2`, Python `py-clob-client-v2`) + +## V2 Migration Notes + +- **Go-live:** April 22, 2026 (~11:00 UTC) +- **Preprod host:** `https://clob-v2.polymarket.com` +- **Prod host after cutover:** `https://clob.polymarket.com` +- **Open orders are wiped during cutover** +- **L1/L2 auth is unchanged** +- **Builder HMAC headers are removed for order attribution**; use `builderCode` +- **Collateral is now pUSD** (not USDC.e) ## API Configuration | API | Base URL | Auth | Purpose | |-----|----------|------|---------| -| CLOB | `https://clob.polymarket.com` | L2 for trade endpoints | Orderbook, prices, order submission | -| Gamma / Data | `https://gamma-api.polymarket.com` | None | Events, markets, search | +| CLOB V2 | `https://clob.polymarket.com` | L2 for trade endpoints | Orderbook, prices, order submission | +| CLOB V2 Test | `https://clob-v2.polymarket.com` | L2 for trade endpoints | Pre-cutover V2 testing | +| Gamma | `https://gamma-api.polymarket.com` | None | Events, markets, search | | Data API | `https://data-api.polymarket.com` | None | Trades, positions, user data | | WebSocket (Market) | `wss://ws-subscriptions-clob.polymarket.com/ws/market` | None | Real-time orderbook | | WebSocket (User) | `wss://ws-subscriptions-clob.polymarket.com/ws/user` | API creds in message | Trade/order updates | | WebSocket (Sports) | `wss://sports-api.polymarket.com/ws` | None | Live scores | -| Relayer | `https://relayer-v2.polymarket.com/` | Builder headers | Gasless transactions | +| Relayer | `https://relayer-v2.polymarket.com/` | Relayer API key or Builder API key | Gasless transactions | | Bridge | `https://bridge.polymarket.com` | None | Deposits/withdrawals | ## Contract Addresses (Polygon) | Contract | Address | |----------|---------| -| USDC (USDC.e) | `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` | -| CTF (Conditional Tokens) | `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045` | -| CTF Exchange | `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` | -| Neg Risk CTF Exchange | `0xC5d563A36AE78145C45a50134d48A1215220f80a` | +| pUSD | `0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB` | +| USDC.e | `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` | +| CTF | `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045` | +| CTF Exchange V2 | `0xE111180000d2663C0091e4f400237545B87B996B` | +| Neg Risk CTF Exchange V2 | `0xe2222d279d744050d28e00520010520000310F59` | | Neg Risk Adapter | `0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296` | +| Collateral Onramp | `0x93070a847efEf7F70739046A929D47a521F5B8ee` | +| Collateral Offramp | `0x2957922Eb93258b93368531d39fAcCA3B4dC5854` | ## Client Setup ### TypeScript ```typescript -import { ClobClient, Side, OrderType } from "@polymarket/clob-client"; -import { Wallet } from "ethers"; // v5.8.0 +import { ClobClient, Side, OrderType } from "@polymarket/clob-client-v2"; +import { Wallet } from "ethers"; -const HOST = "https://clob.polymarket.com"; -const CHAIN_ID = 137; -const signer = new Wallet(process.env.PRIVATE_KEY); +const host = "https://clob.polymarket.com"; +const chain = 137; +const signer = new Wallet(process.env.PRIVATE_KEY!); -// Step 1: L1 — derive API credentials -const tempClient = new ClobClient(HOST, CHAIN_ID, signer); +const tempClient = new ClobClient({ host, chain, signer }); const apiCreds = await tempClient.createOrDeriveApiKey(); -// Step 2: L2 — init trading client -const client = new ClobClient( - HOST, - CHAIN_ID, +const client = new ClobClient({ + host, + chain, signer, - apiCreds, - 2, // signatureType: 0=EOA, 1=POLY_PROXY, 2=GNOSIS_SAFE - "FUNDER_ADDRESS" // proxy wallet address from polymarket.com/settings -); + creds: apiCreds, + signatureType: 2, + funderAddress: process.env.FUNDER_ADDRESS!, + builderConfig: process.env.POLY_BUILDER_CODE + ? { builderCode: process.env.POLY_BUILDER_CODE } + : undefined, +}); ``` ### Python @@ -76,21 +91,19 @@ from py_clob_client.client import ClobClient import os host = "https://clob.polymarket.com" -chain_id = 137 +chain = 137 pk = os.getenv("PRIVATE_KEY") -# Step 1: L1 — derive API credentials -temp_client = ClobClient(host, key=pk, chain_id=chain_id) +temp_client = ClobClient(host=host, chain=chain, key=pk) api_creds = temp_client.create_or_derive_api_creds() -# Step 2: L2 — init trading client client = ClobClient( - host, + host=host, + chain=chain, key=pk, - chain_id=chain_id, creds=api_creds, - signature_type=2, # 0=EOA, 1=POLY_PROXY, 2=GNOSIS_SAFE - funder="FUNDER_ADDRESS", + signature_type=2, + funder=os.getenv("FUNDER_ADDRESS"), ) ``` @@ -99,21 +112,22 @@ client = ClobClient( | Type | Behavior | Use Case | |------|----------|----------| | **GTC** | Rests on book until filled or cancelled | Default limit orders | -| **GTD** | Active until expiration (UTC seconds). Min = `now + 60 + N` | Auto-expire before events | +| **GTD** | Active until expiration (UTC seconds) | Auto-expire before events | | **FOK** | Fill entirely immediately or cancel | All-or-nothing market orders | -| **FAK** | Fill what's available, cancel rest | Partial-fill market orders | +| **FAK** | Fill what is available, cancel rest | Partial-fill market orders | - FOK/FAK BUY: `amount` = dollar amount to spend - FOK/FAK SELL: `amount` = number of shares to sell -- Post-only: GTC/GTD only — rejected if would cross spread +- Market buy orders can include `userUSDCBalance` for fee-aware sizing +- Fees are **not** set in signed orders in V2 ## Quick Reference: Signature Types | Type | Value | Description | |------|-------|-------------| -| EOA | `0` | Standard Ethereum wallet (MetaMask). Funder is the EOA address and will need POL for gas. | -| POLY_PROXY | `1` | Custom proxy wallet for Magic Link email/Google users who exported PK from Polymarket.com. | -| GNOSIS_SAFE | `2` | Gnosis Safe multisig proxy wallet (most common). Use for any new or returning user. | +| EOA | `0` | Standard EOA wallet | +| POLY_PROXY | `1` | Legacy Polymarket proxy wallet | +| GNOSIS_SAFE | `2` | Gnosis Safe multisig proxy wallet | ## Core Pattern: Place an Order @@ -125,12 +139,13 @@ const response = await client.createAndPostOrder( price: 0.50, size: 10, side: Side.BUY, + builderCode: process.env.POLY_BUILDER_CODE, }, { - tickSize: "0.01", // from client.getTickSize(tokenID) or market object - negRisk: false, // from client.getNegRisk(tokenID) or market object + tickSize: "0.01", + negRisk: false, }, - OrderType.GTC + OrderType.GTC, ); console.log(response.orderID, response.status); ``` @@ -152,23 +167,31 @@ print(response["orderID"], response["status"]) ### TypeScript ```typescript -// No auth needed -const readClient = new ClobClient("https://clob.polymarket.com", 137); +const readClient = new ClobClient({ host: "https://clob.polymarket.com", chain: 137 }); const book = await readClient.getOrderBook("TOKEN_ID"); -console.log("Best bid:", book.bids[0], "Best ask:", book.asks[0]); - const mid = await readClient.getMidpoint("TOKEN_ID"); const spread = await readClient.getSpread("TOKEN_ID"); ``` ### Python ```python -read_client = ClobClient("https://clob.polymarket.com", chain_id=137) +read_client = ClobClient(host="https://clob.polymarket.com", chain=137) book = read_client.get_order_book("TOKEN_ID") mid = read_client.get_midpoint("TOKEN_ID") spread = read_client.get_spread("TOKEN_ID") ``` +## Core Pattern: Query V2 Fee / Market Params + +```typescript +const info = await client.getClobMarketInfo("0xCONDITION_ID"); +// info.mts minimum tick size +// info.mos minimum order size +// info.fd fee details { r, e, to } +// info.t tokens +// info.rfqe RFQ enabled +``` + ## Core Pattern: WebSocket Subscribe ```typescript @@ -180,25 +203,16 @@ ws.onopen = () => { assets_ids: ["TOKEN_ID"], custom_feature_enabled: true, })); - // Send PING every 10s to keep alive setInterval(() => ws.send("PING"), 10_000); }; - -ws.onmessage = (event) => { - if (event.data === "PONG") return; - const msg = JSON.parse(event.data); - // msg.event_type: "book" | "price_change" | "last_trade_price" | "tick_size_change" | "best_bid_ask" | "new_market" | "market_resolved" -}; ``` ## Reference files (load on demand) -Only read these when the task requires deeper detail on a specific topic: - -- **Authentication** (L1/L2, builder headers, credential lifecycle): [authentication.md](authentication.md) -- **Order patterns** (GTC/GTD/FOK/FAK, tick sizes, cancel, heartbeat, errors): [order-patterns.md](order-patterns.md) -- **Market data** (Gamma API, Data API, CLOB orderbook, subgraph): [market-data.md](market-data.md) -- **WebSocket** (market/user/sports channels, subscribe, heartbeat): [websocket.md](websocket.md) -- **CTF operations** (split, merge, redeem, neg risk, token IDs): [ctf-operations.md](ctf-operations.md) -- **Bridge** (deposits, withdrawals, supported chains/tokens, status): [bridge.md](bridge.md) -- **Gasless transactions** (relayer client, wallet deployment, builder setup): [gasless.md](gasless.md) +- **Authentication**: [authentication.md](authentication.md) +- **Order patterns**: [order-patterns.md](order-patterns.md) +- **Market data**: [market-data.md](market-data.md) +- **WebSocket**: [websocket.md](websocket.md) +- **CTF operations**: [ctf-operations.md](ctf-operations.md) +- **Bridge**: [bridge.md](bridge.md) +- **Gasless transactions**: [gasless.md](gasless.md) diff --git a/authentication.md b/authentication.md index e0a3a9f..d693e49 100644 --- a/authentication.md +++ b/authentication.md @@ -1,10 +1,18 @@ # Authentication -Polymarket uses two-level auth: **L1** (EIP-712 private key signing) to create credentials, **L2** (HMAC-SHA256 API key signing) to authenticate requests. Builder program adds a separate set of **builder headers** for order attribution and relayer access. +Polymarket CLOB V2 keeps the same two-layer auth model: +- **L1**: EIP-712 wallet signature for API key creation / derivation +- **L2**: HMAC-SHA256 request signing with API credentials -## L1 Authentication (Private Key) +Important V2 clarification: +- **L1/L2 auth is unchanged** from V1 +- **Builder order attribution no longer uses `POLY_BUILDER_*` headers** +- Builders now attach a **`builderCode`** to orders +- Raw relayer HTTP endpoints may still accept Builder API key auth, but normal CLOB trading attribution is now `builderCode`-based -L1 proves wallet ownership via EIP-712 signature. Used to create or derive API credentials. +## L1 Authentication + +L1 proves wallet ownership and is used to create or derive API credentials. ### EIP-712 Domain @@ -23,76 +31,79 @@ const types = { { name: "message", type: "string" }, ], }; - -const value = { - address: signingAddress, // The signing address - timestamp: ts, // The CLOB API server timestamp - nonce: nonce, // The nonce used - message: "This message attests that I control the given wallet", -}; ``` +The auth domain remains version `"1"` in V2. + ### L1 Headers | Header | Description | |--------|-------------| | `POLY_ADDRESS` | Polygon signer address | -| `POLY_SIGNATURE` | CLOB EIP-712 signature | +| `POLY_SIGNATURE` | EIP-712 signature | | `POLY_TIMESTAMP` | Current UNIX timestamp | -| `POLY_NONCE` | Nonce (default: 0) | +| `POLY_NONCE` | Nonce used for deterministic key derivation | ### Create / Derive Credentials ```typescript -// TypeScript -const client = new ClobClient("https://clob.polymarket.com", 137, signer); +import { ClobClient } from "@polymarket/clob-client-v2"; + +const client = new ClobClient({ + host: "https://clob.polymarket.com", + chain: 137, + signer, +}); + const creds = await client.createOrDeriveApiKey(); -// { apiKey: "uuid", secret: "base64...", passphrase: "string" } ``` ```python -# Python -client = ClobClient("https://clob.polymarket.com", key=pk, chain_id=137) +from py_clob_client.client import ClobClient + +client = ClobClient(host="https://clob.polymarket.com", chain=137, key=pk) creds = client.create_or_derive_api_creds() ``` -**REST endpoints:** -- `POST {host}/auth/api-key` — create new credentials (requires L1 headers) -- `GET {host}/auth/derive-api-key` — derive existing credentials (requires L1 headers) +**REST endpoints** +- `POST {host}/auth/api-key` +- `GET {host}/auth/derive-api-key` -## L2 Authentication (API Key) +## L2 Authentication -L2 uses HMAC-SHA256 signatures from the API credentials. Required for all `/v1/trade/*` endpoints. +L2 uses your API credentials for HMAC signing. Required for trading endpoints. -### L2 Headers (all 5 required) +### L2 Headers | Header | Description | |--------|-------------| | `POLY_ADDRESS` | Polygon signer address | | `POLY_SIGNATURE` | HMAC signature for request | | `POLY_TIMESTAMP` | Current UNIX timestamp | -| `POLY_API_KEY` | User's API `apiKey` value | -| `POLY_PASSPHRASE` | User's API `passphrase` value | +| `POLY_API_KEY` | User API key | +| `POLY_PASSPHRASE` | User API passphrase | ### Initialize Trading Client ```typescript -// TypeScript -const client = new ClobClient( - "https://clob.polymarket.com", - 137, +import { ClobClient } from "@polymarket/clob-client-v2"; + +const client = new ClobClient({ + host: "https://clob.polymarket.com", + chain: 137, signer, - apiCreds, // { apiKey, secret, passphrase } - 2, // signatureType - funderAddress // proxy wallet address -); + creds: apiCreds, + signatureType: 2, + funderAddress, +}); ``` ```python -# Python +from py_clob_client.client import ClobClient + client = ClobClient( host="https://clob.polymarket.com", - chain_id=137, + chain=137, key=pk, creds=api_creds, signature_type=2, @@ -104,103 +115,91 @@ client = ClobClient( | Type | Value | When to Use | |------|-------|-------------| -| EOA | `0` | Standard Ethereum wallet (MetaMask). Funder is the EOA address and will need POL to pay gas on transactions. | -| POLY_PROXY | `1` | A custom proxy wallet only used with users who logged in via Magic Link email/Google. Using this requires the user to have exported their PK from Polymarket.com and imported into your app. | -| GNOSIS_SAFE | `2` | Gnosis Safe multisig proxy wallet (most common). Use this for any new or returning user who does not fit the other 2 types. | +| EOA | `0` | Standard externally owned wallet | +| POLY_PROXY | `1` | Legacy Polymarket proxy wallets | +| GNOSIS_SAFE | `2` | Gnosis Safe / proxy wallet flows | -The **funder** is the address holding funds. For proxy wallets, find it at polymarket.com/settings. Proxy wallets are auto-deployed on first Polymarket.com login. +The **funder** is the address holding the funds used for trading. -## Builder Headers +## Builder Attribution in V2 -Builder authentication is separate from L1/L2. Used for order attribution and relayer access. +V1 used Builder HMAC headers plus `builder-signing-sdk` for order attribution. +V2 replaces that with a public **builder code** attached directly to each order. -### Builder Headers (4 required) - -| Header | Description | -|--------|-------------| -| `POLY_BUILDER_API_KEY` | Builder API key | -| `POLY_BUILDER_TIMESTAMP` | Unix timestamp | -| `POLY_BUILDER_PASSPHRASE` | Builder passphrase | -| `POLY_BUILDER_SIGNATURE` | HMAC-SHA256 of request | - -### Initialize Client with Builder Config +### Per-order builder code ```typescript -// TypeScript — local signing -import { BuilderConfig, BuilderApiKeyCreds } from "@polymarket/builder-signing-sdk"; - -const builderCreds: BuilderApiKeyCreds = { - key: process.env.POLY_BUILDER_API_KEY!, - secret: process.env.POLY_BUILDER_SECRET!, - passphrase: process.env.POLY_BUILDER_PASSPHRASE!, -}; +await client.createAndPostOrder( + { + tokenID: "TOKEN_ID", + price: 0.55, + size: 100, + side: Side.BUY, + builderCode: process.env.POLY_BUILDER_CODE, + }, + { tickSize: "0.01", negRisk: false }, +); +``` -const builderConfig = new BuilderConfig({ localBuilderCreds: builderCreds }); +### Client-wide builder code -const client = new ClobClient( - "https://clob.polymarket.com", - 137, +```typescript +const client = new ClobClient({ + host: "https://clob.polymarket.com", + chain: 137, signer, - apiCreds, - 2, + creds: apiCreds, + signatureType: 2, funderAddress, - undefined, - false, - builderConfig -); -// Orders automatically include builder headers + builderConfig: { builderCode: process.env.POLY_BUILDER_CODE! }, +}); ``` -```python -# Python — local signing -from py_builder_signing_sdk import BuilderConfig, BuilderApiKeyCreds - -builder_config = BuilderConfig( - local_builder_creds=BuilderApiKeyCreds( - key=os.environ["POLY_BUILDER_API_KEY"], - secret=os.environ["POLY_BUILDER_SECRET"], - passphrase=os.environ["POLY_BUILDER_PASSPHRASE"], - ) -) +### What changed -client = ClobClient( - host="https://clob.polymarket.com", - chain_id=137, - key=pk, - creds=api_creds, - signature_type=2, - funder=funder_address, - builder_config=builder_config, -) -``` +- removed for CLOB order attribution: + - `POLY_BUILDER_API_KEY` + - `POLY_BUILDER_SECRET` + - `POLY_BUILDER_PASSPHRASE` + - `POLY_BUILDER_SIGNATURE` + - `@polymarket/builder-signing-sdk` +- added: + - `builderCode` on each order, or + - `builderConfig: { builderCode }` on the client -### Remote Signing +## Raw Order Signing Changes -Keep builder credentials on a separate server. Client points to your signing endpoint: +Only relevant if you sign raw order structs yourself instead of using the SDK. -```typescript -// TypeScript client -const builderConfig = new BuilderConfig({ - remoteBuilderConfig: { url: "https://your-server.com/sign" }, -}); -``` +### Exchange domain changes -```python -# Python client -from py_builder_signing_sdk import BuilderConfig, RemoteBuilderConfig +- exchange EIP-712 domain version: `"1"` → `"2"` +- standard exchange verifying contract: + - V1: `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` + - V2: `0xE111180000d2663C0091e4f400237545B87B996B` +- neg risk exchange verifying contract: + - V1: `0xC5d563A36AE78145C45a50134d48A1215220f80a` + - V2: `0xe2222d279d744050d28e00520010520000310F59` -builder_config = BuilderConfig( - remote_builder_config=RemoteBuilderConfig(url="https://your-server.com/sign") -) -``` +### Signed order field changes + +Removed from signed orders: +- `taker` +- `nonce` +- `feeRateBps` +- `expiration` from the EIP-712 struct shown in V1 docs + +Added in V2: +- `timestamp` (ms) +- `metadata` (`bytes32`) +- `builder` (`bytes32`) -Your server receives `{ method, path, body }` and returns the 4 `POLY_BUILDER_*` headers. +If you use the official V2 SDK, this is handled for you. ## Credential Lifecycle -- **Create**: `client.createApiKey()` — generates new credentials with a nonce -- **Derive**: `client.deriveApiKey(nonce)` — recovers existing credentials if you know the nonce -- **Create or Derive**: `client.createOrDeriveApiKey()` — creates if first time, derives if existing -- **Revoke builder key**: `client.revokeBuilderApiKey()` — invalidate compromised builder credentials +- `createApiKey()` — create new API credentials +- `deriveApiKey(nonce)` — derive existing credentials +- `createOrDeriveApiKey()` — preferred setup flow -Lost credentials + lost nonce = create fresh credentials. Save your nonce. +Your existing API keys continue to work in V2. diff --git a/bridge.md b/bridge.md index db9c14d..05028ee 100644 --- a/bridge.md +++ b/bridge.md @@ -1,67 +1,50 @@ # Bridge -Polymarket uses **USDC.e** (Bridged USDC) on Polygon as collateral. The Bridge API handles deposits from and withdrawals to multiple chains. +Polymarket trading collateral is now **pUSD**. The Bridge API handles deposits from supported chains and withdrawals back out. Base URL: `https://bridge.polymarket.com` ## Deposit Flow -1. `POST /deposit` with your Polymarket wallet address → get deposit addresses -2. Verify token is supported via `/supported-assets` -3. Send assets to the appropriate address for your source chain -4. Assets are bridged and auto-swapped to USDC.e on Polygon -5. Track status via `/status/{deposit_address}` +1. `POST /deposit` with your Polymarket wallet address +2. Check `/supported-assets` +3. Send supported funds to the chain-specific deposit address +4. Bridge routes funds to Polygon and credits your Polymarket balance +5. Track progress via `/status/{deposit_address}` + +For end users, bridge flows result in funds becoming usable as **pUSD** on Polymarket. ```bash -# Create deposit addresses curl -X POST https://bridge.polymarket.com/deposit \ -H "Content-Type: application/json" \ -d '{"address": "0xYOUR_POLYMARKET_WALLET"}' ``` -Response includes three address types: +### Deposit address types | Address | Use For | |---------|---------| -| `evm` | Ethereum, Arbitrum, Base, Optimism, and other EVM chains | +| `evm` | EVM chains | | `svm` | Solana | | `btc` | Bitcoin | | `tvm` | Tron | Each address is unique to your wallet. -## Supported Chains - -| Chain | Address Type | Min Deposit | Example Tokens | -|-------|--------------|-------------|----------------| -| Ethereum | EVM | $7 | ETH, USDC, USDT, WBTC, DAI, LINK, UNI, AAVE | -| Polygon | EVM | $2 | POL, USDC, USDT, DAI, WETH, SAND | -| Arbitrum | EVM | $2 | ETH, ARB, USDC, USDT, DAI, WBTC, USDe | -| Base | EVM | $2 | ETH, USDC, USDT, DAI, cbBTC, AERO, USDS | -| Optimism | EVM | $2 | ETH, OP, USDC, USDT, DAI, USDe | -| BNB Smart Chain | EVM | $2 | BNB, USDC, USDT, DAI, ETH, BTCB, BUSD | -| Solana | SVM | $2 | SOL, USDC, USDT, USDe, TRUMP | -| Bitcoin | BTC | $9 | BTC | -| Tron | TVM | $9 | USDT | -| HyperEVM | EVM | $2 | HYPE, USDC, USDe, stHYPE, UBTC, UETH | -| Abstract | EVM | $2 | ETH, USDC, USDT | -| Monad | EVM | $2 | MON, USDC, USDT | -| Ethereal | EVM | $2 | USDe, WUSDe | -| Katana | EVM | $2 | AUSD | -| Lighter | EVM | $2 | USDC | - -Always call `/supported-assets` for the current list — assets change over time. +## Supported chains + +Always call `/supported-assets` for the live list. +Examples currently documented include Ethereum, Polygon, Arbitrum, Base, Optimism, BNB Smart Chain, Solana, Bitcoin, Tron, HyperEVM, Abstract, Monad, Ethereal, Katana, and Lighter. ## Withdrawal Flow -1. Check destination chain/token via `/supported-assets` -2. Preview fees via `POST /quote` -3. `POST /withdraw` with wallet address, destination chain, token, and recipient → get deposit addresses -4. Send USDC.e from Polymarket wallet to the appropriate address -5. Track status via `/status/{address}` +1. Check `/supported-assets` +2. Preview using `POST /quote` +3. `POST /withdraw` with destination chain, token, and recipient +4. Send funds from your Polymarket wallet to the provided withdrawal address +5. Poll `/status/{address}` until completion ```bash -# Create withdrawal addresses curl -X POST https://bridge.polymarket.com/withdraw \ -H "Content-Type: application/json" \ -d '{ @@ -72,37 +55,36 @@ curl -X POST https://bridge.polymarket.com/withdraw \ }' ``` -**Do not pre-generate withdrawal addresses.** Generate them only when ready to execute. +Do not pre-generate withdrawal addresses too early; create them only when ready to use. ## Quote -Preview fees and estimated output for deposits and withdrawals. Withdrawals are **instant** and **free** — Polymarket does not charge withdrawal fees. - ```bash POST https://bridge.polymarket.com/quote ``` +Use quotes before deposits or withdrawals to inspect fees and expected output. + ## Status Tracking ```bash -# Use the deposit address (not your wallet address) curl https://bridge.polymarket.com/status/0xDEPOSIT_ADDRESS ``` -### Transaction Statuses +### Statuses | Status | Terminal | Description | |--------|----------|-------------| -| `DEPOSIT_DETECTED` | No | Funds detected on source chain, not yet processing | -| `PROCESSING` | No | Being routed and swapped | -| `ORIGIN_TX_CONFIRMED` | No | Source chain transaction confirmed | -| `SUBMITTED` | No | Submitted to Polygon | -| `COMPLETED` | Yes | Funds arrived — success | -| `FAILED` | Yes | Error occurred | +| `DEPOSIT_DETECTED` | No | Source funds detected | +| `PROCESSING` | No | Routing / swap in progress | +| `ORIGIN_TX_CONFIRMED` | No | Source chain tx confirmed | +| `SUBMITTED` | No | Submitted on destination side | +| `COMPLETED` | Yes | Success | +| `FAILED` | Yes | Failure | -Poll every 10–30 seconds until `COMPLETED` or `FAILED`. +Poll every 10–30 seconds until terminal. -### Response +## Example response ```json { @@ -111,7 +93,7 @@ Poll every 10–30 seconds until `COMPLETED` or `FAILED`. "fromTokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "fromAmountBaseUnit": "1000000000", "toChainId": "137", - "toTokenAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + "toTokenAddress": "0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB", "status": "COMPLETED", "txHash": "0x...", "createdTimeMs": 1697875200000 @@ -119,19 +101,17 @@ Poll every 10–30 seconds until `COMPLETED` or `FAILED`. } ``` -Empty `transactions` array = no deposits detected yet. +Note: destination collateral on Polymarket is now **pUSD**, so expect pUSD-related destination references instead of legacy USDC.e-only assumptions. ## Recovery -If you deposited the wrong token: -- **Ethereum deposits**: https://recovery.polymarket.com/ -- **Polygon deposits**: https://matic-recovery.polymarket.com/ - -Sending unsupported tokens may cause **irrecoverable loss**. +If you deposit the wrong token: +- Ethereum deposits: `https://recovery.polymarket.com/` +- Polygon deposits: `https://matic-recovery.polymarket.com/` ## Caveats -- **Withdrawals >$50,000**: break into smaller amounts to minimize slippage -- **Uniswap pool exhaustion**: USDC.e → USDC swap goes through Uniswap v3 pool. If pool is exhausted, use smaller amounts or wait for rebalance -- **Deposits below minimum**: will not be processed -- **Supported assets change**: always check `/supported-assets` before depositing +- deposits below minimum are not processed +- supported assets change; always check `/supported-assets` +- large withdrawals may incur slippage depending on route and token +- for API-only workflows, remember Polymarket trading collateral is pUSD diff --git a/ctf-operations.md b/ctf-operations.md index 154cce3..56c877c 100644 --- a/ctf-operations.md +++ b/ctf-operations.md @@ -1,6 +1,11 @@ # CTF Operations -The **Conditional Token Framework (CTF)** creates ERC1155 tokens for market outcomes. Three core operations: split, merge, redeem. +The Conditional Token Framework (CTF) powers Polymarket outcome tokens. In V2, positions are collateralized with **pUSD**. + +Core operations: +- **split** — pUSD → Yes + No +- **merge** — Yes + No → pUSD +- **redeem** — winning tokens → pUSD after resolution ## Token Model @@ -8,78 +13,68 @@ Every binary market has two tokens: | Token | Redeems for | Condition | |-------|-------------|-----------| -| **Yes** | $1.00 USDC.e | Event occurs | -| **No** | $1.00 USDC.e | Event does not occur | +| **Yes** | $1.00 pUSD | Event occurs | +| **No** | $1.00 pUSD | Event does not occur | -Every Yes/No pair is backed by exactly $1.00 USDC.e locked in the CTF contract. +Every Yes/No pair is fully collateralized by pUSD locked in the CTF system. ## Split -Convert USDC.e into a full set of outcome tokens. +Convert pUSD into a full set of outcome tokens. -``` -$100 USDC.e → 100 Yes tokens + 100 No tokens +```text +$100 pUSD → 100 Yes tokens + 100 No tokens ``` ### Prerequisites -1. USDC.e balance on Polygon -2. USDC.e approval for CTF contract -3. Condition ID of the market (the condition must already be prepared on the CTF contract via `prepareCondition`) +1. pUSD balance on Polygon +2. pUSD approval for the CTF contract +3. Market condition ID ### Function: `splitPosition` | Parameter | Type | Value | |-----------|------|-------| -| `collateralToken` | IERC20 | `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` (USDC.e) | -| `parentCollectionId` | bytes32 | `0x0000...0000` (32 zero bytes) | -| `conditionId` | bytes32 | Market's condition ID | -| `partition` | uint[] | `[1, 2]` for binary (Yes=1, No=2) | -| `amount` | uint256 | Amount of USDC.e to split | +| `collateralToken` | IERC20 | `0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB` (pUSD) | +| `parentCollectionId` | bytes32 | `0x0000...0000` | +| `conditionId` | bytes32 | Market condition ID | +| `partition` | uint[] | `[1, 2]` for binary markets | +| `amount` | uint256 | Amount of pUSD to split | ## Merge -Convert a full set of outcome tokens back to USDC.e. Inverse of split. +Convert a full set of outcome tokens back into pUSD. -``` -100 Yes tokens + 100 No tokens → $100 USDC.e +```text +100 Yes + 100 No → $100 pUSD ``` -### Prerequisites -1. Equal amounts of both Yes and No tokens -2. Condition ID (the condition must already be prepared on the CTF contract via `prepareCondition`) -3. Sufficient gas for the transaction - ### Function: `mergePositions` -Same parameters as split. Burns one unit of each position per unit of collateral returned. +Uses the same parameters as split. One full set burns into one unit of pUSD collateral. ## Redeem -Exchange winning tokens for USDC.e after market resolution. +After resolution, winning tokens redeem into pUSD. -``` +```text Market resolves YES: - 100 Yes tokens → $100 USDC.e - 100 No tokens → $0 +100 Yes → $100 pUSD +100 No → $0 ``` -### Prerequisites -1. Market must be resolved -2. Hold winning tokens -3. Know the condition ID - ### Function: `redeemPositions` | Parameter | Type | Value | |-----------|------|-------| -| `collateralToken` | IERC20 | USDC.e address | +| `collateralToken` | IERC20 | pUSD | | `parentCollectionId` | bytes32 | `0x0000...0000` | -| `conditionId` | bytes32 | Market's condition ID | -| `indexSets` | uint[] | `[1, 2]` — redeems both (only winner pays) | +| `conditionId` | bytes32 | Market condition ID | +| `indexSets` | uint[] | `[1, 2]` | -Redemption burns your **entire** token balance for the condition — no amount parameter. No deadline — winning tokens are always redeemable. +Redemption burns your full balance for that condition. There is no amount parameter. -### Payout Vectors +## Payout Vectors | Outcome | Payout Vector | Redemption | |---------|---------------|------------| @@ -90,91 +85,49 @@ Redemption burns your **entire** token balance for the condition — no amount p | Contract | Address | Purpose | |----------|---------|---------| -| CTF | `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045` | Token storage and operations | -| USDC.e (Bridged USDC) | `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` | Collateral token | -| CTF Exchange | `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` | Standard market trading | -| Neg Risk CTF Exchange | `0xC5d563A36AE78145C45a50134d48A1215220f80a` | Neg risk market trading | +| pUSD | `0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB` | Trading collateral | +| USDC.e | `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` | Wrap source asset for API-only flows | +| CTF | `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045` | Token storage and split/merge/redeem | +| CTF Exchange V2 | `0xE111180000d2663C0091e4f400237545B87B996B` | Standard market trading | +| Neg Risk CTF Exchange V2 | `0xe2222d279d744050d28e00520010520000310F59` | Neg risk market trading | | Neg Risk Adapter | `0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296` | Neg risk conversions | +| Collateral Onramp | `0x93070a847efEf7F70739046A929D47a521F5B8ee` | Wrap USDC.e → pUSD | +| Collateral Offramp | `0x2957922Eb93258b93368531d39fAcCA3B4dC5854` | Unwrap pUSD → USDC.e | ## Approval Matrix -Before trading or CTF operations, the funder must approve the relevant contracts: - | Operation | Contract to Approve | Token | -|-----------|-------------------|-------| -| Buy order (standard) | CTF Exchange | USDC.e | -| Sell order (standard) | CTF Exchange | Conditional tokens | -| Buy order (neg risk) | Neg Risk CTF Exchange | USDC.e | -| Sell order (neg risk) | Neg Risk CTF Exchange | Conditional tokens | -| Split | CTF | USDC.e | +|-----------|---------------------|-------| +| Buy order (standard) | CTF Exchange V2 | pUSD | +| Sell order (standard) | CTF Exchange V2 | Conditional tokens | +| Buy order (neg risk) | Neg Risk CTF Exchange V2 | pUSD | +| Sell order (neg risk) | Neg Risk CTF Exchange V2 | Conditional tokens | +| Split | CTF | pUSD | | Neg risk conversion | Neg Risk Adapter | Conditional tokens | - -## Standard vs Neg Risk Markets - -| Feature | Standard Markets | Neg Risk Markets | -|---------|-----------------|------------------| -| CTF Contract | ConditionalTokens | ConditionalTokens | -| Exchange Contract | CTF Exchange | Neg Risk CTF Exchange | -| Multi-outcome | Independent markets | Linked via conversion | -| `negRisk` flag | `false` | `true` | -| Order option | `negRisk: false` | `negRisk: true` | +| Wrap | Collateral Onramp | USDC.e | +| Unwrap | Collateral Offramp | pUSD | ## Negative Risk -Multi-outcome events where only one outcome can win. A No token in any market can be **converted** into 1 Yes token in every other market. - -### Conversion Example - -Event: "Who wins?" with outcomes Trump, Harris, Other. - -| Outcome | Before | After Conversion | -|---------|--------|------------------| -| Trump | — | 1 Yes | -| Harris | — | 1 Yes | -| Other | 1 No | — | - -Conversion is atomic through the Neg Risk Adapter contract. - -### Identifying Neg Risk Markets - -```json -{ - "negRisk": true // on event or market object from API -} -``` - -When placing orders: pass `negRisk: true` in options. - -## Augmented Negative Risk - -For events where new outcomes emerge after trading begins (e.g., new candidate enters race). - -| Outcome Type | Description | -|--------------|-------------| -| Named outcomes | Known outcomes (e.g., "Trump", "Harris") | -| Placeholder outcomes | Reserved slots clarified later (e.g., "Person A") | -| Explicit Other | Catches any unnamed outcome | +Neg risk markets are linked multi-outcome markets where a No token can convert into baskets of Yes tokens in other outcomes. -### Identifying +### Identifying a neg risk market ```json { - "enableNegRisk": true, - "negRiskAugmented": true + "negRisk": true } ``` -### Rules -- Only trade on **named outcomes** — ignore placeholders -- If correct outcome is not named at resolution, market resolves to "Other" -- "Other" definition changes as placeholders are clarified — avoid trading it directly +When placing orders, pass `negRisk: true` in order options. ## Token ID Computation -Token IDs are computed onchain in three steps: +Position IDs are computed as: +1. `getConditionId(oracle, questionId, outcomeSlotCount)` +2. `getCollectionId(parentCollectionId, conditionId, indexSet)` +3. `getPositionId(collateralToken, collectionId)` -1. `getConditionId(oracle, questionId, outcomeSlotCount)` — oracle = UMA CTF Adapter, outcomeSlotCount = 2 for binary -2. `getCollectionId(parentCollectionId, conditionId, indexSet)` — parentCollectionId = bytes32(0), indexSet = 1 (Yes) or 2 (No) -3. `getPositionId(collateralToken, collectionId)` — combines USDC.e contract address on Polygon with collection +In V2, the `collateralToken` for Polymarket positions is **pUSD**. -In practice, get token IDs from the Markets API `tokens` array. Manual computation only needed for direct contract interaction. +In practice, prefer token IDs returned by the Gamma API `tokens` array. diff --git a/gasless.md b/gasless.md index 14b52f1..ef62b3c 100644 --- a/gasless.md +++ b/gasless.md @@ -1,46 +1,69 @@ # Gasless Transactions -Polymarket's **Relayer Client** enables gasless transactions. Instead of requiring users to hold POL, Polymarket's infrastructure pays gas fees. Users only need USDC.e to trade. +Polymarket's relayer enables gasless onchain actions. Users do not need POL for gas; Polymarket sponsors execution. -Requires **Builder Program** membership. You need Builder API credentials. +For trading collateral, users need **pUSD**. + +## What changed in V2 + +- trading collateral is **pUSD**, not USDC.e +- recommended SDK auth uses **Relayer API Keys** +- CLOB order attribution moved to `builderCode`, but relayer HTTP endpoints can still support Builder API Key auth at the raw API level +- recommended relayer client setup now uses an options object ## How It Works -1. Your app creates a transaction -2. User signs it with their private key -3. App sends to Polymarket's relayer -4. Relayer submits onchain and pays gas -5. Transaction executes from the user's wallet +1. Your app prepares one or more transactions +2. The user signs through their wallet +3. Your app submits through the relayer +4. Polymarket pays gas +5. The transaction executes from the user's wallet / proxy -## What's Covered +## Covered operations | Operation | Description | |-----------|-------------| -| Wallet deployment | Deploy Safe or Proxy wallets for new users | -| Token approvals | Approve contracts to spend USDC.e or outcome tokens | -| CTF operations | Split, merge, redeem positions | -| Transfers | Move tokens between addresses | +| Wallet deployment | Deploy Safe or Proxy wallets | +| Token approvals | Approve pUSD or outcome tokens | +| CTF operations | Split, merge, redeem | +| Transfers | Move assets | + +## Authentication + +### Recommended: Relayer API Keys + +Create a Relayer API key from Polymarket settings. + +Headers: + +| Header | Description | +|--------|-------------| +| `RELAYER_API_KEY` | Relayer API key | +| `RELAYER_API_KEY_ADDRESS` | Address that owns the key | + +### Raw API note + +The relayer API reference also allows Builder API Key auth on some endpoints, but for SDK integrations the current documented path is **Relayer API Keys**. ## Installation ```bash # TypeScript -npm install @polymarket/builder-relayer-client @polymarket/builder-signing-sdk +npm install @polymarket/builder-relayer-client # Python -pip install py-builder-relayer-client py-builder-signing-sdk +pip install py-builder-relayer-client ``` ## Client Setup -### TypeScript (Local Signing) +### TypeScript ```typescript import { createWalletClient, http, Hex } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { polygon } from "viem/chains"; import { RelayClient } from "@polymarket/builder-relayer-client"; -import { BuilderConfig } from "@polymarket/builder-signing-sdk"; const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex); const wallet = createWalletClient({ @@ -49,133 +72,85 @@ const wallet = createWalletClient({ transport: http(process.env.RPC_URL), }); -const builderConfig = new BuilderConfig({ - localBuilderCreds: { - key: process.env.POLY_BUILDER_API_KEY!, - secret: process.env.POLY_BUILDER_SECRET!, - passphrase: process.env.POLY_BUILDER_PASSPHRASE!, - }, +const client = new RelayClient({ + host: "https://relayer-v2.polymarket.com/", + chain: 137, + signer: wallet, + relayerApiKey: process.env.RELAYER_API_KEY!, + relayerApiKeyAddress: process.env.RELAYER_API_KEY_ADDRESS!, }); - -const client = new RelayClient( - "https://relayer-v2.polymarket.com/", - 137, - wallet, - builderConfig -); ``` -### Python (Local Signing) +### Python ```python import os from py_builder_relayer_client.client import RelayClient -from py_builder_signing_sdk import BuilderConfig, BuilderApiKeyCreds - -builder_config = BuilderConfig( - local_builder_creds=BuilderApiKeyCreds( - key=os.getenv("POLY_BUILDER_API_KEY"), - secret=os.getenv("POLY_BUILDER_SECRET"), - passphrase=os.getenv("POLY_BUILDER_PASSPHRASE"), - ) -) client = RelayClient( - "https://relayer-v2.polymarket.com", - 137, - os.getenv("PRIVATE_KEY"), - builder_config, -) -``` - -### Remote Signing - -Keep credentials on your server. Client points to signing endpoint: - -```typescript -// TypeScript -const builderConfig = new BuilderConfig({ - remoteBuilderConfig: { url: "https://your-server.com/sign" }, -}); - -const client = new RelayClient( - "https://relayer-v2.polymarket.com/", - 137, - wallet, - builderConfig -); -``` - -```python -# Python -from py_builder_signing_sdk import BuilderConfig, RemoteBuilderConfig - -builder_config = BuilderConfig( - remote_builder_config=RemoteBuilderConfig(url="https://your-server.com/sign") + host="https://relayer-v2.polymarket.com", + chain=137, + signer=os.getenv("PRIVATE_KEY"), + relayer_api_key=os.environ["RELAYER_API_KEY"], + relayer_api_key_address=os.environ["RELAYER_API_KEY_ADDRESS"], ) - -client = RelayClient("https://relayer-v2.polymarket.com", 137, pk, builder_config) ``` ## Wallet Types | Type | Deployment | Best For | |------|------------|----------| -| **Safe** | Call `deploy()` before first transaction | Most builder integrations | -| **Proxy** | Auto-deploys on first transaction | Magic Link users | +| **Safe** | Call `deploy()` first | Most integrations | +| **Proxy** | Auto-deploy on first tx | Proxy-wallet flows | ```typescript -// TypeScript — Safe wallet import { RelayClient, RelayerTxType } from "@polymarket/builder-relayer-client"; -const client = new RelayClient( - "https://relayer-v2.polymarket.com/", - 137, - wallet, - builderConfig, - RelayerTxType.SAFE -); +const client = new RelayClient({ + host: "https://relayer-v2.polymarket.com/", + chain: 137, + signer: wallet, + relayerApiKey: process.env.RELAYER_API_KEY!, + relayerApiKeyAddress: process.env.RELAYER_API_KEY_ADDRESS!, + txType: RelayerTxType.SAFE, +}); -// Deploy before first transaction const response = await client.deploy(); const result = await response.wait(); -console.log("Safe Address:", result?.proxyAddress); -``` - -```python -# Python — Safe wallet -response = client.deploy() -result = response.wait() -print("Safe Address:", result.get("proxyAddress")) +console.log(result?.proxyAddress); ``` ## Executing Transactions ```typescript interface Transaction { - to: string; // Target contract address - data: string; // Encoded function call - value: string; // POL to send (usually "0") + to: string; + data: string; + value: string; } const response = await client.execute(transactions, "Description"); -const result = await response.wait(); +await response.wait(); ``` -### Token Approval Example +### Approval example ```typescript import { encodeFunctionData, maxUint256 } from "viem"; -const USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; +const pUSD = "0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"; const CTF = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"; const approveTx = { - to: USDC, + to: pUSD, data: encodeFunctionData({ abi: [{ - name: "approve", type: "function", - inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }], + name: "approve", + type: "function", + inputs: [ + { name: "spender", type: "address" }, + { name: "amount", type: "uint256" }, + ], outputs: [{ type: "bool" }], }], functionName: "approve", @@ -184,53 +159,35 @@ const approveTx = { value: "0", }; -const response = await client.execute([approveTx], "Approve USDC.e for CTF"); -await response.wait(); -``` - -### Batch Transactions - -Execute multiple operations atomically in a single call: - -```typescript -const response = await client.execute( - [approveTx, transferTx], - "Approve and transfer" -); -await response.wait(); +await client.execute([approveTx], "Approve pUSD for CTF"); ``` ## Transaction States | State | Terminal | Description | |-------|----------|-------------| -| `STATE_NEW` | No | Received by relayer | +| `STATE_NEW` | No | Received | | `STATE_EXECUTED` | No | Submitted onchain | | `STATE_MINED` | No | Included in a block | | `STATE_CONFIRMED` | Yes | Finalized successfully | -| `STATE_FAILED` | Yes | Failed permanently | -| `STATE_INVALID` | Yes | Rejected as invalid | - -## Builder Setup - -1. Go to polymarket.com/settings?tab=builder -2. Create builder profile + generate API keys -3. Implement builder signing in your CLOB client -4. All orders automatically attributed to your builder account +| `STATE_FAILED` | Yes | Permanent failure | +| `STATE_INVALID` | Yes | Rejected | ## Contract Addresses | Contract | Address | Approval Needed | |----------|---------|-----------------| -| USDC.e (Bridged USDC) | `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` | — | -| CTF | `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045` | USDC.e | -| CTF Exchange | `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` | USDC.e, Tokens | -| Neg Risk CTF Exchange | `0xC5d563A36AE78145C45a50134d48A1215220f80a` | USDC.e, Tokens | -| Neg Risk Adapter | `0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296` | Tokens | - -## SDKs - -- [Builder Relayer Client (TypeScript)](https://github.com/Polymarket/builder-relayer-client) -- [Builder Relayer Client (Python)](https://github.com/Polymarket/py-builder-relayer-client) -- [Builder Signing SDK (TypeScript)](https://github.com/Polymarket/builder-signing-sdk) -- [Builder Signing SDK (Python)](https://github.com/Polymarket/py-builder-signing-sdk) +| pUSD | `0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB` | — | +| USDC.e | `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` | Wrap source asset | +| CTF | `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045` | pUSD | +| CTF Exchange V2 | `0xE111180000d2663C0091e4f400237545B87B996B` | pUSD, tokens | +| Neg Risk CTF Exchange V2 | `0xe2222d279d744050d28e00520010520000310F59` | pUSD, tokens | +| Neg Risk Adapter | `0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296` | tokens | +| Collateral Onramp | `0x93070a847efEf7F70739046A929D47a521F5B8ee` | approve USDC.e | +| Collateral Offramp | `0x2957922Eb93258b93368531d39fAcCA3B4dC5854` | approve pUSD | + +## Notes + +- keep Relayer API keys server-side +- relayer base URL remains `https://relayer-v2.polymarket.com/` +- for API-only users, wrap USDC.e to pUSD before trading if needed diff --git a/market-data.md b/market-data.md index d8ed471..86f44db 100644 --- a/market-data.md +++ b/market-data.md @@ -1,216 +1,217 @@ # Market Data -Four sources for market data: **Gamma API** (events, markets, search), **Data API** (trades, positions, user data), **CLOB** (orderbook, prices), and **Subgraph** (onchain queries). +Polymarket market data comes from four main sources: +- **Gamma API** — events, markets, search, metadata +- **Data API** — trades, positions, user-related data +- **CLOB API** — orderbook, prices, spreads, fees, CLOB market info +- **Subgraph** — onchain analytics and historical queries ## Gamma API -Base URL: `https://gamma-api.polymarket.com` — no auth required. +Base URL: `https://gamma-api.polymarket.com` -### Events Endpoint +### Events ```bash -# All active events -GET https://gamma-api.polymarket.com/events?active=true&closed=false&limit=100 - -# By slug (from polymarket.com/event/{slug}) +# By slug GET https://gamma-api.polymarket.com/events?slug=fed-decision-in-october # By tag -GET https://gamma-api.polymarket.com/events?tag_id=100381&limit=10&active=true&closed=false +GET https://gamma-api.polymarket.com/events?tag_id=100381&closed=false&limit=10 -# By series (sports) -GET https://gamma-api.polymarket.com/events?series_id=10345&active=true&closed=false - -# Sorted by volume -GET https://gamma-api.polymarket.com/events?active=true&closed=false&order=volume_24hr&ascending=false&limit=100 +# By series +GET https://gamma-api.polymarket.com/events?series_id=10345&closed=false&limit=20 ``` -### Markets Endpoint +### Markets ```bash # By slug GET https://gamma-api.polymarket.com/markets?slug=fed-decision-in-october ``` -### Sort Parameters +## Recommended pagination: keyset endpoints -| Parameter | Values | -|-----------|--------| -| `order` | `volume_24hr`, `volume`, `liquidity`, `start_date`, `end_date`, `competitive`, `closed_time` | -| `ascending` | `true` / `false` (default: `false`) | -| `active` | `true` / `false` | -| `closed` | `true` / `false` | -| `limit` | 1–500 (default: 20) | -| `offset` | Pagination offset | +Polymarket now documents **keyset pagination** for stable paging through large datasets. +Prefer these over offset-based pagination when scanning many rows. -### Pagination +### Events keyset pagination ```bash -# Page 1 -GET https://gamma-api.polymarket.com/events?active=true&closed=false&limit=50&offset=0 +GET https://gamma-api.polymarket.com/events/keyset?closed=false&limit=50 +GET https://gamma-api.polymarket.com/events/keyset?closed=false&limit=50&after_cursor=NEXT_CURSOR +``` -# Page 2 -GET https://gamma-api.polymarket.com/events?active=true&closed=false&limit=50&offset=50 +### Markets keyset pagination + +```bash +GET https://gamma-api.polymarket.com/markets/keyset?closed=false&limit=100 +GET https://gamma-api.polymarket.com/markets/keyset?closed=false&limit=100&after_cursor=NEXT_CURSOR ``` -Response includes `has_more: true/false`. Increment offset by limit until `has_more` is `false`. +Rules: +- use `next_cursor` from the previous response as `after_cursor` +- `offset` is explicitly rejected on keyset endpoints +- max limits: + - events keyset: `500` + - markets keyset: `1000` + +### Common filters -### Tags & Sports +| Parameter | Notes | +|-----------|-------| +| `slug` | Fetch a specific event or market | +| `tag_id` / `tag_slug` | Category filters | +| `closed` | Historical vs active | +| `order` | Sort field(s) | +| `ascending` | Sort direction | +| `limit` | Page size | +| `after_cursor` | Keyset pagination cursor | + +### Tags & Sports metadata ```bash -# Discover tags GET https://gamma-api.polymarket.com/tags - -# Sports metadata GET https://gamma-api.polymarket.com/sports ``` ## Data API -Base URL: `https://data-api.polymarket.com` — no auth required. Used for trades, positions, and user-specific data. +Base URL: `https://data-api.polymarket.com` + +Used for trades, positions, activity, and user-related reporting endpoints. + +## CLOB Market Data -## CLOB Orderbook +Base URL: `https://clob.polymarket.com` -Base URL: `https://clob.polymarket.com` — no auth for read endpoints. +No auth required for read-only endpoints. -### Get Orderbook +### Read client setup ```typescript -// TypeScript -const client = new ClobClient("https://clob.polymarket.com", 137); -const book = await client.getOrderBook("TOKEN_ID"); -// { bids: [{price, size}...], asks: [{price, size}...], tick_size, min_order_size, neg_risk } +import { ClobClient } from "@polymarket/clob-client-v2"; + +const client = new ClobClient({ + host: "https://clob.polymarket.com", + chain: 137, +}); ``` ```python -# Python -client = ClobClient("https://clob.polymarket.com", chain_id=137) -book = client.get_order_book("TOKEN_ID") -``` +from py_clob_client.client import ClobClient -```bash -# REST -curl "https://clob.polymarket.com/book?token_id=TOKEN_ID" +client = ClobClient(host="https://clob.polymarket.com", chain=137) ``` -### Prices +### Orderbook ```typescript -const buyPrice = await client.getPrice("TOKEN_ID", "BUY"); // best ask -const sellPrice = await client.getPrice("TOKEN_ID", "SELL"); // best bid +const book = await client.getOrderBook("TOKEN_ID"); ``` ```bash -curl "https://clob.polymarket.com/price?token_id=TOKEN_ID&side=BUY" +curl "https://clob.polymarket.com/book?token_id=TOKEN_ID" ``` -### Midpoint +### Prices ```typescript -const mid = await client.getMidpoint("TOKEN_ID"); // { mid: "0.50" } +const buyPrice = await client.getPrice("TOKEN_ID", "BUY"); // best ask +const sellPrice = await client.getPrice("TOKEN_ID", "SELL"); // best bid +const mid = await client.getMidpoint("TOKEN_ID"); +const spread = await client.getSpread("TOKEN_ID"); +const last = await client.getLastTradePrice("TOKEN_ID"); ``` -If bid-ask spread > $0.10, Polymarket UI shows last traded price instead of midpoint. +### Batch reads -### Spread +All major market-data methods have batch variants. -```typescript -const spread = await client.getSpread("TOKEN_ID"); // { spread: "0.04" } -``` +| Single | Batch | REST | +|--------|-------|------| +| `getOrderBook()` | `getOrderBooks()` | `POST /books` | +| `getPrice()` | `getPrices()` | `POST /prices` | +| `getMidpoint()` | `getMidpoints()` | `POST /midpoints` | +| `getSpread()` | `getSpreads()` | `POST /spreads` | +| `getLastTradePrice()` | `getLastTradesPrices()` | request body variant documented | + +## V2 CLOB market info -### Last Trade Price +V2 adds `getClobMarketInfo()`, which exposes market-specific CLOB parameters in one call. ```typescript -const last = await client.getLastTradePrice("TOKEN_ID"); // { price, side } +const info = await client.getClobMarketInfo("0xCONDITION_ID"); +// info.mts minimum tick size +// info.mos minimum order size +// info.fd fee details { r, e, to } +// info.t tokens [{ t, o }] +// info.rfqe request-for-quote enabled ``` -### Price History - -```typescript -const history = await client.getPricesHistory({ - market: "TOKEN_ID", - interval: PriceHistoryInterval.ONE_DAY, - fidelity: 60, // data points every 60 minutes -}); -// Each entry: { t: timestamp, p: price } +```bash +curl "https://clob.polymarket.com/clob-markets/0xCONDITION_ID" ``` -| Interval | Description | -|----------|-------------| -| `1h` | Last hour | -| `6h` | Last 6 hours | -| `1d` | Last day | -| `1w` | Last week | -| `1m` | Last month | -| `max` | All available | +Useful fields: +- `mts` — minimum tick size +- `mos` — minimum order size +- `fd` — fee curve params +- `t` — token IDs and outcomes +- `rfqe` — RFQ enabled +- `mbf` / `tbf` — base fees -Use `startTs`/`endTs` for absolute ranges (mutually exclusive with `interval`). +## Fee model notes -### Estimate Fill Price +In V2, fees are not embedded in the signed order. +They are determined by protocol market parameters at match time. +For manual inspection, use `getClobMarketInfo()`. -Walk the orderbook to estimate slippage for a given order size: +## Price history ```typescript -const price = await client.calculateMarketPrice( - "TOKEN_ID", Side.BUY, 500, OrderType.FOK -); +const history = await client.getPricesHistory({ + market: "TOKEN_ID", + interval: PriceHistoryInterval.ONE_DAY, + fidelity: 60, +}); ``` -### Batch Requests - -All orderbook queries have batch variants (up to 500 tokens): - -| Single | Batch | REST | -|--------|-------|------| -| `getOrderBook()` | `getOrderBooks()` | `POST /books` | -| `getPrice()` | `getPrices()` | `POST /prices` | -| `getMidpoint()` | `getMidpoints()` | `POST /midpoints` | -| `getSpread()` | `getSpreads()` | `POST /spreads` | -| `getLastTradePrice()` | `getLastTradesPrices()` | — | - -```typescript -const prices = await client.getPrices([ - { token_id: "TOKEN_A", side: Side.BUY }, - { token_id: "TOKEN_B", side: Side.BUY }, -]); -``` +Intervals commonly used: +- `1h` +- `6h` +- `1d` +- `1w` +- `1m` +- `max` ## Key Market Fields | Field | Description | |-------|-------------| -| `tokenID` / `asset_id` | ERC1155 token ID for an outcome | -| `conditionID` / `market` | Condition ID — identifies the market | -| `questionID` | Hash of UMA ancillary data | -| `neg_risk` | `true` for multi-outcome events | -| `minimum_tick_size` | Minimum price increment | -| `enableOrderBook` | Whether orderbook is active | -| `slug` | URL-friendly identifier | -| `tokens` | Array of `{ token_id, outcome }` for both outcomes | - -## Subgraph (Onchain Data) - -GraphQL queries via Goldsky-hosted subgraphs: - -| Subgraph | Description | -|----------|-------------| -| Positions | User token balances | -| Orders | Order book and trade events | -| Activity | Splits, merges, redemptions | -| Open Interest | Market and global OI | -| PNL | User position P&L | +| `tokenID` / `asset_id` | Outcome token ID | +| `conditionID` / `market` | Condition ID | +| `tokens` | Array of outcome token objects | +| `neg_risk` / `negRisk` | Neg risk market flag | +| `minimum_tick_size` | Tick size | +| `enableOrderBook` | Orderbook enabled | +| `slug` | URL slug | + +## Subgraph + +Useful for onchain analytics, open interest, PnL, balances, and historical event data. ```bash curl -X POST \ https://api.goldsky.com/api/public/project_cl6mb8i9h0003e201j6li0diw/subgraphs/orderbook-subgraph/0.0.1/gn \ -H "Content-Type: application/json" \ - -d '{"query": "query { orderbooks { id tradesQuantity } }"}' + -d '{"query":"query { orderbooks { id tradesQuantity } }"}' ``` -## Fetching Strategy +## Practical fetching strategy -1. **Specific market**: fetch by slug — `GET https://gamma-api.polymarket.com/events?slug=...` -2. **Category browsing**: filter by tag — `GET https://gamma-api.polymarket.com/events?tag_id=...` -3. **All active markets**: paginate events — `GET https://gamma-api.polymarket.com/events?active=true&closed=false` -4. **Always include** `active=true&closed=false` unless you need historical data -5. **Events > Markets**: events contain their markets, reducing API calls +1. Fetch a known event by `slug` +2. For browsing, use `events/keyset` or `markets/keyset` +3. Pull token IDs from Gamma responses +4. Query CLOB orderbook / prices using token IDs +5. Use `getClobMarketInfo()` when you need tick size, fee params, minimum order size, or token mappings diff --git a/order-patterns.md b/order-patterns.md index 1fcf048..2632ed4 100644 --- a/order-patterns.md +++ b/order-patterns.md @@ -1,42 +1,58 @@ # Order Patterns -All orders on Polymarket are expressed as limit orders. Market orders are limit orders with a marketable price that execute immediately. +All Polymarket CLOB V2 orders are expressed as limit-style orders. Market orders are marketable limit orders that execute immediately or cancel. + +## V2 Changes That Matter + +- SDK package is now `@polymarket/clob-client-v2` / `py-clob-client-v2` +- fees are **not embedded** in signed orders +- do **not** set `feeRateBps`, `nonce`, or `taker` +- builders use `builderCode` +- collateral for buying is **pUSD** +- if you manually sign raw orders, the exchange EIP-712 domain version is now `"2"` ## Order Types | Type | Behavior | Use Case | |------|----------|----------| -| **GTC** | Good-Til-Cancelled — rests on book until filled or cancelled | Default for limit orders | -| **GTD** | Good-Til-Date — active until expiration timestamp (UTC seconds), unless filled or cancelled first | Auto-expire before known events | -| **FOK** | Fill-Or-Kill — fill entirely immediately or cancel | All-or-nothing market orders | -| **FAK** | Fill-And-Kill — fill what's available, cancel rest | Partial-fill market orders | +| **GTC** | Good-Til-Cancelled | Default resting limit orders | +| **GTD** | Good-Til-Date | Auto-expiring limit orders | +| **FOK** | Fill-Or-Kill | All-or-nothing marketable order | +| **FAK** | Fill-And-Kill | Partial-fill marketable order | ## Tick Sizes -Price must conform to the market's tick size or the order is rejected. +| Tick Size | Precision | Example | +|-----------|-----------|---------| +| `0.1` | 1 decimal | `0.5` | +| `0.01` | 2 decimals | `0.50` | +| `0.001` | 3 decimals | `0.500` | +| `0.0001` | 4 decimals | `0.5000` | -| Tick Size | Precision | Example Prices | -|-----------|-----------|----------------| -| `0.1` | 1 decimal | 0.1, 0.2, 0.5 | -| `0.01` | 2 decimals | 0.01, 0.50, 0.99 | -| `0.001` | 3 decimals | 0.001, 0.500, 0.999 | -| `0.0001` | 4 decimals | 0.0001, 0.5000, 0.9999 | - -Get tick size: `client.getTickSize(tokenID)` (TS) / `client.get_tick_size(token_id)` (Python). Also available as `minimum_tick_size` on market objects. +Fetch tick size from: +- `client.getTickSize(tokenID)` +- `minimum_tick_size` on market objects +- `client.getClobMarketInfo(conditionID).mts` ## Limit Order (GTC) ```typescript -// TypeScript — one-step +import { ClobClient, Side, OrderType } from "@polymarket/clob-client-v2"; + const response = await client.createAndPostOrder( - { tokenID: "TOKEN_ID", price: 0.50, size: 10, side: Side.BUY }, + { + tokenID: "TOKEN_ID", + price: 0.50, + size: 10, + side: Side.BUY, + builderCode: process.env.POLY_BUILDER_CODE, + }, { tickSize: "0.01", negRisk: false }, - OrderType.GTC + OrderType.GTC, ); ``` ```python -# Python — one-step response = client.create_and_post_order( OrderArgs(token_id="TOKEN_ID", price=0.50, size=10, side=BUY), options={"tick_size": "0.01", "neg_risk": False}, @@ -44,185 +60,103 @@ response = client.create_and_post_order( ) ``` -### Two-step (sign then submit) +## Two-step flow ```typescript -// TypeScript const signedOrder = await client.createOrder( { tokenID: "TOKEN_ID", price: 0.50, size: 10, side: Side.BUY }, - { tickSize: "0.01", negRisk: false } + { tickSize: "0.01", negRisk: false }, ); -const response = await client.postOrder(signedOrder, OrderType.GTC); -``` -```python -# Python -signed_order = client.create_order( - OrderArgs(token_id="TOKEN_ID", price=0.50, size=10, side=BUY), - options={"tick_size": "0.01", "neg_risk": False}, -) -response = client.post_order(signed_order, OrderType.GTC) +const response = await client.postOrder(signedOrder, OrderType.GTC); ``` -## Market Order (FOK / FAK) +## Market Orders (FOK / FAK) -- **BUY**: `amount` = dollar amount to spend -- **SELL**: `amount` = number of shares to sell -- `price` = worst-price limit (slippage protection), not target execution price +- **BUY**: `amount` is the dollar amount to spend +- **SELL**: `amount` is the number of shares to sell +- optional `userUSDCBalance` helps fee-aware market-buy calculations in V2 ```typescript -// TypeScript — FOK BUY: spend exactly $100 or cancel -const buyOrder = await client.createMarketOrder( - { tokenID: "TOKEN_ID", side: Side.BUY, amount: 100, price: 0.50 }, - { tickSize: "0.01", negRisk: false } -); -await client.postOrder(buyOrder, OrderType.FOK); - -// One-step convenience const response = await client.createAndPostMarketOrder( - { tokenID: "TOKEN_ID", side: Side.BUY, amount: 100, price: 0.50 }, + { + tokenID: "TOKEN_ID", + side: Side.BUY, + amount: 100, + price: 0.50, + userUSDCBalance: 1000, + builderCode: process.env.POLY_BUILDER_CODE, + }, { tickSize: "0.01", negRisk: false }, - OrderType.FOK + OrderType.FOK, ); ``` -```python -# Python — FOK BUY -buy_order = client.create_market_order( - token_id="TOKEN_ID", side=BUY, amount=100, price=0.50, - options={"tick_size": "0.01", "neg_risk": False}, -) -client.post_order(buy_order, OrderType.FOK) -``` - -## GTD Order (Expiring) - -Expiration = UTC seconds timestamp. Security threshold: add 60 seconds minimum. +## GTD Orders -**Effective lifetime of N seconds: `now + 60 + N`** +Expiration remains a UNIX timestamp in seconds. ```typescript -// TypeScript — expire in 1 hour const expiration = Math.floor(Date.now() / 1000) + 60 + 3600; -const response = await client.createAndPostOrder( +await client.createAndPostOrder( { tokenID: "TOKEN_ID", price: 0.50, size: 10, side: Side.BUY, expiration }, { tickSize: "0.01", negRisk: false }, - OrderType.GTD + OrderType.GTD, ); ``` -```python -# Python — expire in 1 hour -import time -expiration = int(time.time()) + 60 + 3600 - -response = client.create_and_post_order( - OrderArgs(token_id="TOKEN_ID", price=0.50, size=10, side=BUY, expiration=expiration), - options={"tick_size": "0.01", "neg_risk": False}, - order_type=OrderType.GTD, -) -``` - -## Post-Only Orders - -Guarantee maker status. If order would cross spread, it's rejected (not executed). +## Post-only Orders ```typescript -// TypeScript const response = await client.postOrder(signedOrder, OrderType.GTC, true); ``` -```python -# Python -response = client.post_order(signed_order, OrderType.GTC, post_only=True) -``` - -- Only works with GTC and GTD -- Rejected if combined with FOK or FAK +Rules: +- valid with `GTC` and `GTD` +- rejected with `FOK` / `FAK` +- rejected if it would cross the spread ## Batch Orders -Up to **15 orders** in a single request. +Maximum: **15 orders** per request. ```typescript -// TypeScript -const orders: PostOrdersArgs[] = [ +const orders = [ { order: await client.createOrder( { tokenID: "TOKEN_ID", price: 0.48, side: Side.BUY, size: 500 }, - { tickSize: "0.01", negRisk: false } + { tickSize: "0.01", negRisk: false }, ), orderType: OrderType.GTC, }, { order: await client.createOrder( { tokenID: "TOKEN_ID", price: 0.52, side: Side.SELL, size: 500 }, - { tickSize: "0.01", negRisk: false } + { tickSize: "0.01", negRisk: false }, ), orderType: OrderType.GTC, }, ]; -const response = await client.postOrders(orders); -``` -```python -# Python -response = client.post_orders([ - PostOrdersArgs( - order=client.create_order( - OrderArgs(price=0.48, size=500, side=BUY, token_id="TOKEN_ID"), - options={"tick_size": "0.01", "neg_risk": False}, - ), - orderType=OrderType.GTC, - ), - PostOrdersArgs( - order=client.create_order( - OrderArgs(price=0.52, size=500, side=SELL, token_id="TOKEN_ID"), - options={"tick_size": "0.01", "neg_risk": False}, - ), - orderType=OrderType.GTC, - ), -]) +await client.postOrders(orders); ``` ## Cancel Orders -All cancel endpoints require L2 authentication. - ```typescript -// TypeScript -await client.cancelOrder("0xORDER_ID"); // single -await client.cancelOrders(["0xID_1", "0xID_2"]); // multiple -await client.cancelAll(); // all orders -await client.cancelMarketOrders({ market: "0xCONDITION_ID" }); // by market -await client.cancelMarketOrders({ // by token - market: "0xCONDITION_ID", - asset_id: "TOKEN_ID", -}); +await client.cancelOrder("0xORDER_ID"); +await client.cancelOrders(["0xID_1", "0xID_2"]); +await client.cancelAll(); +await client.cancelMarketOrders({ market: "0xCONDITION_ID" }); +await client.cancelMarketOrders({ market: "0xCONDITION_ID", asset_id: "TOKEN_ID" }); ``` -```python -# Python -client.cancel(order_id="0xORDER_ID") -client.cancel_orders(["0xID_1", "0xID_2"]) -client.cancel_all() -client.cancel_market_orders( - market="0xCONDITION_ID", - asset_id="TOKEN_ID", # optional -) -``` - -### Onchain Cancellation (fallback) - -If the API is unavailable, cancel directly on the Exchange contract by calling `cancelOrder(Order order)` onchain with the full signed order struct. Use the `CTFExchange` or `NegRiskCTFExchange` contract depending on the market type. See [Contract Addresses](/resources/contract-addresses) for addresses. - ## Heartbeat -If a valid heartbeat is not received within **10 seconds** (with up to a 5-second buffer), **all of your open orders will be cancelled**. +If no valid heartbeat is received within roughly 10 seconds (plus small buffer), open orders are cancelled. ```typescript -// TypeScript let heartbeatId = ""; setInterval(async () => { const resp = await client.postHeartbeat(heartbeatId); @@ -230,74 +164,51 @@ setInterval(async () => { }, 5000); ``` -```python -# Python -import time -heartbeat_id = "" -while True: - resp = client.post_heartbeat(heartbeat_id) - heartbeat_id = resp["heartbeat_id"] - time.sleep(5) -``` +## Trade / Insert Statuses -- First request: use empty string for `heartbeat_id` -- If you send an invalid or expired `heartbeat_id`, the server responds with a `400 Bad Request` and provides the correct `heartbeat_id` in the response - -## Error Codes - -| Error | Description | -|-------|-------------| -| `INVALID_ORDER_MIN_TICK_SIZE` | Price doesn't conform to the market's tick size | -| `INVALID_ORDER_MIN_SIZE` | Order size is below the minimum threshold | -| `INVALID_ORDER_DUPLICATED` | Identical order has already been placed | -| `INVALID_ORDER_NOT_ENOUGH_BALANCE` | Funder doesn't have sufficient balance or allowance | -| `INVALID_ORDER_EXPIRATION` | Expiration timestamp is in the past | -| `INVALID_ORDER_ERROR` | System error while inserting order | -| `INVALID_POST_ONLY_ORDER_TYPE` | Post-only flag used with a market order type (FOK/FAK) | -| `INVALID_POST_ONLY_ORDER` | Post-only order would cross the book | -| `EXECUTION_ERROR` | System error while executing trade | -| `ORDER_DELAYED` | Order placement delayed due to market conditions | -| `DELAYING_ORDER_ERROR` | System error while delaying order | -| `FOK_ORDER_NOT_FILLED_ERROR` | FOK order couldn't be fully filled | -| `MARKET_NOT_READY` | Market is not yet accepting orders | - -## Insert Statuses - -| Status | Description | -|--------|-------------| -| `matched` | Order placed and matched with a resting order | -| `live` | Order placed and resting on the book | -| `delayed` | Order is marketable but subject to a matching delay | -| `unmatched` | Order is marketable but failed to delay — placement still successful | - -## Trade Statuses +### Insert statuses +- `matched` +- `live` +- `delayed` +- `unmatched` -``` -MATCHED → MINED → CONFIRMED - ↓ ↑ -RETRYING ───┘ - ↓ - FAILED -``` +### Trade lifecycle +- `MATCHED` +- `MINED` +- `CONFIRMED` +- `RETRYING` +- `FAILED` -| Status | Terminal | Description | -|--------|----------|-------------| -| `MATCHED` | No | Matched and sent to the executor service for onchain submission | -| `MINED` | No | Observed as mined on the chain, no finality threshold yet | -| `CONFIRMED` | Yes | Achieved strong probabilistic finality — trade successful | -| `RETRYING` | No | Transaction failed (revert or reorg) — being retried by the operator | -| `FAILED` | Yes | Trade failed permanently and is not being retried | +## Errors + +Common documented errors include: +- `INVALID_ORDER_MIN_TICK_SIZE` +- `INVALID_ORDER_MIN_SIZE` +- `INVALID_ORDER_DUPLICATED` +- `INVALID_ORDER_NOT_ENOUGH_BALANCE` +- `INVALID_ORDER_EXPIRATION` +- `INVALID_POST_ONLY_ORDER_TYPE` +- `INVALID_POST_ONLY_ORDER` +- `FOK_ORDER_NOT_FILLED_ERROR` +- `MARKET_NOT_READY` ## Prerequisites -Before placing orders, the funder address must approve the Exchange contract: -- **Buying**: the funder must have set a **USDC.e** allowance greater than or equal to the spending amount. -- **Selling**: the funder must have set a **conditional token** allowance greater than or equal to the selling amount. +Before placing orders, the funder address must approve the exchange contract. + +- **Buying**: approve **pUSD** allowance for the appropriate exchange +- **Selling**: approve conditional-token allowance for the appropriate exchange + +Relevant exchange contracts: +- standard: `0xE111180000d2663C0091e4f400237545B87B996B` +- neg risk: `0xe2222d279d744050d28e00520010520000310F59` -Max order size = `balance - sum(openOrderSize - filledAmount)` +## Raw signing checklist -## Sports Markets +If you manually build EIP-712 orders: +- use exchange domain version `"2"` +- use V2 exchange verifying contract +- include `timestamp`, `metadata`, `builder` +- remove `nonce`, `feeRateBps`, `taker` -- Outstanding limit orders auto-cancelled when game begins -- Marketable orders have 3-second placement delay -- Game start times can shift — monitor accordingly +If you use the official V2 SDK, this is automatic. From 1e72f12f4529175676eaa8f55eaac37a53f1cb73 Mon Sep 17 00:00:00 2001 From: Sayo <82053242+wtfsayo@users.noreply.github.com> Date: Sat, 18 Apr 2026 12:36:15 +0530 Subject: [PATCH 2/2] Preserve skill reference patterns while correcting V2 migration details The V2 docs update was directionally correct, but it flattened some of the original skill-manual structure and left a few migration-guide mismatches in place. This commit restores the progressive-disclosure/reference style while keeping the content aligned to Polymarket's V2 documentation. It also fixes snippet-level issues called out by review and by source validation: the `getLastTradePrices()` batch method name, missing snippet imports, and clarification that `userUSDCBalance` remains the documented SDK field name even though V2 trading collateral is pUSD. Constraint: Scope limited to documentation updates on the existing V2 migration branch Rejected: Revert to pre-migration docs structure wholesale | would reintroduce V1 guidance Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep the original skill-doc reference density unless a source-backed change requires simplification Tested: Source validation against docs.polymarket.com/v2-migration.md and docs.polymarket.com/llms.txt; manual diff review Not-tested: Runtime execution of SDK snippets against installed Polymarket client packages Related: https://github.com/Polymarket/agent-skills/pull/1 --- README.md | 42 ++++++++++---------- SKILL.md | 18 +++++---- authentication.md | 19 ++++++++++ market-data.md | 43 ++++++++++++++++++++- order-patterns.md | 97 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 191 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 80887bb..136c194 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@ Agent skill for building on Polymarket CLOB V2. Covers authentication, trading, ``` web3-polymarket/ -├── SKILL.md -├── README.md -├── authentication.md -├── order-patterns.md -├── market-data.md -├── websocket.md -├── ctf-operations.md -├── bridge.md -└── gasless.md +├── SKILL.md # Entry point — quick reference, client setup, core patterns +├── README.md # This file +├── authentication.md # L1/L2 auth, builderCode attribution, relayer auth notes +├── order-patterns.md # Order types, tick sizes, cancel, heartbeat, errors +├── market-data.md # Gamma API, Data API, CLOB orderbook, fee params, subgraph +├── websocket.md # Market/user/sports channels, subscribe, heartbeat +├── ctf-operations.md # Split, merge, redeem, negative risk, token IDs +├── bridge.md # Deposits, withdrawals, supported chains/tokens +└── gasless.md # Relayer client, wallet deployment, builder / relayer auth ``` ## V2 status @@ -34,8 +34,12 @@ Key V2 changes reflected here: ## How It Works -1. **SKILL.md loads first** for quick-reference setup and core flows. -2. **Reference files load on demand** for deeper detail. +The skill uses **progressive disclosure** to stay efficient with context: + +1. **SKILL.md loads first** — contains API endpoints, contract addresses, client setup, and core code patterns. Enough for most tasks. +2. **Reference files load on demand** — when a task needs deeper detail, the agent reads the relevant file. + +This keeps the initial context small while preserving the richer reference material in the topic files. ## When Agents Use This Skill @@ -150,14 +154,14 @@ const response = await tradingClient.createAndPostOrder( | File | Use it when you need to... | |------|----------------------------| -| [SKILL.md](SKILL.md) | Get the quick-reference V2 setup | -| [authentication.md](authentication.md) | Understand L1/L2 auth and builderCode attribution | -| [order-patterns.md](order-patterns.md) | Create, submit, cancel, and maintain orders | -| [market-data.md](market-data.md) | Query events, markets, CLOB data, and pagination | -| [websocket.md](websocket.md) | Stream market and user updates | -| [ctf-operations.md](ctf-operations.md) | Split, merge, and redeem pUSD-backed positions | -| [bridge.md](bridge.md) | Deposit, withdraw, and track bridge flows | -| [gasless.md](gasless.md) | Use the relayer for gasless onchain actions | +| [SKILL.md](SKILL.md) | Get started — has the quick-reference V2 setup | +| [authentication.md](authentication.md) | Understand L1/L2 auth, builderCode attribution, or relayer auth details | +| [order-patterns.md](order-patterns.md) | Use advanced order types (GTD, post-only, batch), handle errors, or implement heartbeat | +| [market-data.md](market-data.md) | Query markets by slug/tag, paginate results, inspect fee params, or estimate fill prices | +| [websocket.md](websocket.md) | Stream real-time orderbook updates, trade notifications, or sports scores | +| [ctf-operations.md](ctf-operations.md) | Split/merge/redeem tokens, work with neg risk markets, or compute token IDs | +| [bridge.md](bridge.md) | Deposit from other chains, withdraw, check supported assets, or track transaction status | +| [gasless.md](gasless.md) | Set up gas-free transactions via the relayer, deploy wallets, or configure relayer auth | ## SDKs diff --git a/SKILL.md b/SKILL.md index 94c56a8..b3226e8 100644 --- a/SKILL.md +++ b/SKILL.md @@ -118,7 +118,7 @@ client = ClobClient( - FOK/FAK BUY: `amount` = dollar amount to spend - FOK/FAK SELL: `amount` = number of shares to sell -- Market buy orders can include `userUSDCBalance` for fee-aware sizing +- Market buy orders can include `userUSDCBalance` for fee-aware sizing; the field name is legacy, but the value should reflect available **pUSD** trading collateral - Fees are **not** set in signed orders in V2 ## Quick Reference: Signature Types @@ -209,10 +209,12 @@ ws.onopen = () => { ## Reference files (load on demand) -- **Authentication**: [authentication.md](authentication.md) -- **Order patterns**: [order-patterns.md](order-patterns.md) -- **Market data**: [market-data.md](market-data.md) -- **WebSocket**: [websocket.md](websocket.md) -- **CTF operations**: [ctf-operations.md](ctf-operations.md) -- **Bridge**: [bridge.md](bridge.md) -- **Gasless transactions**: [gasless.md](gasless.md) +Only read these when the task requires deeper detail on a specific topic: + +- **Authentication** (L1/L2, builderCode attribution, relayer auth details): [authentication.md](authentication.md) +- **Order patterns** (GTC/GTD/FOK/FAK, tick sizes, cancel, heartbeat, errors): [order-patterns.md](order-patterns.md) +- **Market data** (Gamma API, Data API, CLOB orderbook, fee params, subgraph): [market-data.md](market-data.md) +- **WebSocket** (market/user/sports channels, subscribe, heartbeat): [websocket.md](websocket.md) +- **CTF operations** (split, merge, redeem, neg risk, token IDs): [ctf-operations.md](ctf-operations.md) +- **Bridge** (deposits, withdrawals, supported chains/tokens, status): [bridge.md](bridge.md) +- **Gasless transactions** (relayer client, wallet deployment, auth): [gasless.md](gasless.md) diff --git a/authentication.md b/authentication.md index d693e49..f301cb1 100644 --- a/authentication.md +++ b/authentication.md @@ -129,6 +129,8 @@ V2 replaces that with a public **builder code** attached directly to each order. ### Per-order builder code ```typescript +import { ClobClient, Side } from "@polymarket/clob-client-v2"; + await client.createAndPostOrder( { tokenID: "TOKEN_ID", @@ -167,6 +169,23 @@ const client = new ClobClient({ - `builderCode` on each order, or - `builderConfig: { builderCode }` on the client +## Raw Relayer Builder Auth + +For normal CLOB order attribution, use `builderCode`. +For raw relayer HTTP endpoints, the official docs still describe Builder API key auth and Relayer API key auth. + +### Builder / Relayer auth headers + +| Header | Description | +|--------|-------------| +| `POLY_BUILDER_API_KEY` | Builder API key identifier | +| `POLY_BUILDER_PASSPHRASE` | Builder passphrase | +| `POLY_BUILDER_SIGNATURE` | Builder HMAC signature | +| `POLY_BUILDER_TIMESTAMP` | Current UNIX timestamp | + +Use these only when you are integrating with the relayer directly. +Do not treat them as the order-attribution path for CLOB V2 orders. + ## Raw Order Signing Changes Only relevant if you sign raw order structs yourself instead of using the SDK. diff --git a/market-data.md b/market-data.md index 86f44db..855f1ca 100644 --- a/market-data.md +++ b/market-data.md @@ -13,6 +13,9 @@ Base URL: `https://gamma-api.polymarket.com` ### Events ```bash +# All active events +GET https://gamma-api.polymarket.com/events?active=true&closed=false&limit=100 + # By slug GET https://gamma-api.polymarket.com/events?slug=fed-decision-in-october @@ -21,6 +24,9 @@ GET https://gamma-api.polymarket.com/events?tag_id=100381&closed=false&limit=10 # By series GET https://gamma-api.polymarket.com/events?series_id=10345&closed=false&limit=20 + +# Sorted by volume +GET https://gamma-api.polymarket.com/events?active=true&closed=false&order=volume_24hr&ascending=false&limit=100 ``` ### Markets @@ -30,6 +36,17 @@ GET https://gamma-api.polymarket.com/events?series_id=10345&closed=false&limit=2 GET https://gamma-api.polymarket.com/markets?slug=fed-decision-in-october ``` +### Sort Parameters + +| Parameter | Values | +|-----------|--------| +| `order` | `volume_24hr`, `volume`, `liquidity`, `start_date`, `end_date`, `competitive`, `closed_time` | +| `ascending` | `true` / `false` (default: `false`) | +| `active` | `true` / `false` | +| `closed` | `true` / `false` | +| `limit` | endpoint-specific | +| `offset` | legacy offset pagination on non-keyset endpoints | + ## Recommended pagination: keyset endpoints Polymarket now documents **keyset pagination** for stable paging through large datasets. @@ -71,7 +88,10 @@ Rules: ### Tags & Sports metadata ```bash +# Discover tags GET https://gamma-api.polymarket.com/tags + +# Sports metadata GET https://gamma-api.polymarket.com/sports ``` @@ -124,6 +144,8 @@ const spread = await client.getSpread("TOKEN_ID"); const last = await client.getLastTradePrice("TOKEN_ID"); ``` +If the spread is wide, consuming both midpoint and last-trade price can produce better UI behavior than midpoint alone. + ### Batch reads All major market-data methods have batch variants. @@ -134,7 +156,7 @@ All major market-data methods have batch variants. | `getPrice()` | `getPrices()` | `POST /prices` | | `getMidpoint()` | `getMidpoints()` | `POST /midpoints` | | `getSpread()` | `getSpreads()` | `POST /spreads` | -| `getLastTradePrice()` | `getLastTradesPrices()` | request body variant documented | +| `getLastTradePrice()` | `getLastTradePrices()` | request body variant documented | ## V2 CLOB market info @@ -170,6 +192,8 @@ For manual inspection, use `getClobMarketInfo()`. ## Price history ```typescript +import { PriceHistoryInterval } from "@polymarket/clob-client-v2"; + const history = await client.getPricesHistory({ market: "TOKEN_ID", interval: PriceHistoryInterval.ONE_DAY, @@ -185,6 +209,23 @@ Intervals commonly used: - `1m` - `max` +Use `startTs` / `endTs` for absolute ranges when you do not want interval-based presets. + +## Estimate Fill Price + +Walk the orderbook to estimate slippage for a given order size: + +```typescript +import { OrderType, Side } from "@polymarket/clob-client-v2"; + +const price = await client.calculateMarketPrice( + "TOKEN_ID", + Side.BUY, + 500, + OrderType.FOK, +); +``` + ## Key Market Fields | Field | Description | diff --git a/order-patterns.md b/order-patterns.md index 2632ed4..b855cf0 100644 --- a/order-patterns.md +++ b/order-patterns.md @@ -53,6 +53,9 @@ const response = await client.createAndPostOrder( ``` ```python +from py_clob_client.clob_types import OrderArgs, OrderType +from py_clob_client.order_builder.constants import BUY + response = client.create_and_post_order( OrderArgs(token_id="TOKEN_ID", price=0.50, size=10, side=BUY), options={"tick_size": "0.01", "neg_risk": False}, @@ -71,11 +74,20 @@ const signedOrder = await client.createOrder( const response = await client.postOrder(signedOrder, OrderType.GTC); ``` +```python +signed_order = client.create_order( + OrderArgs(token_id="TOKEN_ID", price=0.50, size=10, side=BUY), + options={"tick_size": "0.01", "neg_risk": False}, +) +response = client.post_order(signed_order, OrderType.GTC) +``` + ## Market Orders (FOK / FAK) - **BUY**: `amount` is the dollar amount to spend - **SELL**: `amount` is the number of shares to sell - optional `userUSDCBalance` helps fee-aware market-buy calculations in V2 +- the field name is legacy; in V2 the value supplied should reflect the user's available **pUSD** trading collateral balance ```typescript const response = await client.createAndPostMarketOrder( @@ -92,6 +104,17 @@ const response = await client.createAndPostMarketOrder( ); ``` +```python +buy_order = client.create_market_order( + token_id="TOKEN_ID", + side=BUY, + amount=100, + price=0.50, + options={"tick_size": "0.01", "neg_risk": False}, +) +client.post_order(buy_order, OrderType.FOK) +``` + ## GTD Orders Expiration remains a UNIX timestamp in seconds. @@ -106,12 +129,28 @@ await client.createAndPostOrder( ); ``` +```python +import time + +expiration = int(time.time()) + 60 + 3600 + +response = client.create_and_post_order( + OrderArgs(token_id="TOKEN_ID", price=0.50, size=10, side=BUY, expiration=expiration), + options={"tick_size": "0.01", "neg_risk": False}, + order_type=OrderType.GTD, +) +``` + ## Post-only Orders ```typescript const response = await client.postOrder(signedOrder, OrderType.GTC, true); ``` +```python +response = client.post_order(signed_order, OrderType.GTC, post_only=True) +``` + Rules: - valid with `GTC` and `GTD` - rejected with `FOK` / `FAK` @@ -142,6 +181,28 @@ const orders = [ await client.postOrders(orders); ``` +```python +from py_clob_client.clob_types import OrderArgs, OrderType, PostOrdersArgs +from py_clob_client.order_builder.constants import BUY, SELL + +response = client.post_orders([ + PostOrdersArgs( + order=client.create_order( + OrderArgs(price=0.48, size=500, side=BUY, token_id="TOKEN_ID"), + options={"tick_size": "0.01", "neg_risk": False}, + ), + orderType=OrderType.GTC, + ), + PostOrdersArgs( + order=client.create_order( + OrderArgs(price=0.52, size=500, side=SELL, token_id="TOKEN_ID"), + options={"tick_size": "0.01", "neg_risk": False}, + ), + orderType=OrderType.GTC, + ), +]) +``` + ## Cancel Orders ```typescript @@ -152,6 +213,16 @@ await client.cancelMarketOrders({ market: "0xCONDITION_ID" }); await client.cancelMarketOrders({ market: "0xCONDITION_ID", asset_id: "TOKEN_ID" }); ``` +```python +client.cancel(order_id="0xORDER_ID") +client.cancel_orders(["0xID_1", "0xID_2"]) +client.cancel_all() +client.cancel_market_orders( + market="0xCONDITION_ID", + asset_id="TOKEN_ID", +) +``` + ## Heartbeat If no valid heartbeat is received within roughly 10 seconds (plus small buffer), open orders are cancelled. @@ -164,6 +235,20 @@ setInterval(async () => { }, 5000); ``` +```python +import time + +heartbeat_id = "" +while True: + resp = client.post_heartbeat(heartbeat_id) + heartbeat_id = resp["heartbeat_id"] + time.sleep(5) +``` + +Notes: +- first request uses an empty heartbeat id +- if you send an invalid or expired heartbeat id, the server returns the current one in the error response + ## Trade / Insert Statuses ### Insert statuses @@ -192,6 +277,12 @@ Common documented errors include: - `FOK_ORDER_NOT_FILLED_ERROR` - `MARKET_NOT_READY` +Typical meanings: +- `INVALID_ORDER_MIN_TICK_SIZE` — price does not match the market tick size +- `INVALID_ORDER_MIN_SIZE` — order size is below the market minimum +- `INVALID_ORDER_NOT_ENOUGH_BALANCE` — insufficient balance or allowance +- `INVALID_POST_ONLY_ORDER` — post-only order would cross the book + ## Prerequisites Before placing orders, the funder address must approve the exchange contract. @@ -203,6 +294,12 @@ Relevant exchange contracts: - standard: `0xE111180000d2663C0091e4f400237545B87B996B` - neg risk: `0xe2222d279d744050d28e00520010520000310F59` +## Sports Markets + +- outstanding limit orders may be cancelled when the game begins +- marketable orders may be placement-delayed around game state changes +- scheduled start times can shift, so avoid hardcoding assumptions around exact open / close transitions + ## Raw signing checklist If you manually build EIP-712 orders: