add skills

This commit is contained in:
Suhail Kakar
2026-02-19 21:29:35 +05:30
committed by GitHub
commit 91ee44ae11
9 changed files with 1855 additions and 0 deletions
+145
View File
@@ -0,0 +1,145 @@
# 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.
## 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
```
## How It Works
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 (e.g., full error code list, bridge chain support, WebSocket event schemas), the agent reads the relevant file.
This keeps the initial context small (~200 lines) while giving access to ~1,700 lines of detailed reference material when needed.
## When Agents Use This Skill
An agent activates this skill when a user asks about:
- **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
## Quick Start for Humans
If you're a developer reading this directly (not an agent), here's the fastest path:
### 1. Install the SDK
```bash
# TypeScript
npm install @polymarket/clob-client ethers@5.8.0
# Python
pip install py-clob-client
```
### 2. Get API Credentials
```typescript
import { ClobClient } from "@polymarket/clob-client";
import { Wallet } from "ethers";
const client = new ClobClient(
"https://clob.polymarket.com",
137,
new Wallet(process.env.PRIVATE_KEY)
);
const creds = await client.createOrDeriveApiKey();
```
### 3. Place an Order
```typescript
const tradingClient = new ClobClient(
"https://clob.polymarket.com",
137,
signer,
creds,
2, // GNOSIS_SAFE (most common)
"FUNDER_ADDR" // from polymarket.com/settings
);
const response = await tradingClient.createAndPostOrder(
{ tokenID: "TOKEN_ID", price: 0.50, size: 10, side: "BUY" },
{ tickSize: "0.01", negRisk: false },
"GTC"
);
```
## Key Concepts
| 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 |
## API Endpoints
| API | Base URL | Auth Required |
|-----|----------|---------------|
| CLOB | `https://clob.polymarket.com` | L2 headers 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 |
| 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 |
## Contract Addresses (Polygon)
| Contract | Address |
|----------|---------|
| USDC.e (Bridged USDC) | `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` |
| CTF | `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045` |
| CTF Exchange | `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` |
| Neg Risk CTF Exchange | `0xC5d563A36AE78145C45a50134d48A1215220f80a` |
| Neg Risk Adapter | `0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296` |
## 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 |
## 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)
- **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)
+204
View File
@@ -0,0 +1,204 @@
---
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
---
# Polymarket Skill
## When to use this skill
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)
- 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)
## 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 |
| 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 |
| 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` |
| Neg Risk Adapter | `0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296` |
## Client Setup
### TypeScript
```typescript
import { ClobClient, Side, OrderType } from "@polymarket/clob-client";
import { Wallet } from "ethers"; // v5.8.0
const HOST = "https://clob.polymarket.com";
const CHAIN_ID = 137;
const signer = new Wallet(process.env.PRIVATE_KEY);
// Step 1: L1 — derive API credentials
const tempClient = new ClobClient(HOST, CHAIN_ID, signer);
const apiCreds = await tempClient.createOrDeriveApiKey();
// Step 2: L2 — init trading client
const client = new ClobClient(
HOST,
CHAIN_ID,
signer,
apiCreds,
2, // signatureType: 0=EOA, 1=POLY_PROXY, 2=GNOSIS_SAFE
"FUNDER_ADDRESS" // proxy wallet address from polymarket.com/settings
);
```
### Python
```python
from py_clob_client.client import ClobClient
import os
host = "https://clob.polymarket.com"
chain_id = 137
pk = os.getenv("PRIVATE_KEY")
# Step 1: L1 — derive API credentials
temp_client = ClobClient(host, key=pk, chain_id=chain_id)
api_creds = temp_client.create_or_derive_api_creds()
# Step 2: L2 — init trading client
client = ClobClient(
host,
key=pk,
chain_id=chain_id,
creds=api_creds,
signature_type=2, # 0=EOA, 1=POLY_PROXY, 2=GNOSIS_SAFE
funder="FUNDER_ADDRESS",
)
```
## Quick Reference: Order Types
| 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 |
| **FOK** | Fill entirely immediately or cancel | All-or-nothing market orders |
| **FAK** | Fill what's 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
## 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. |
## Core Pattern: Place an Order
### TypeScript
```typescript
const response = await client.createAndPostOrder(
{
tokenID: "TOKEN_ID",
price: 0.50,
size: 10,
side: Side.BUY,
},
{
tickSize: "0.01", // from client.getTickSize(tokenID) or market object
negRisk: false, // from client.getNegRisk(tokenID) or market object
},
OrderType.GTC
);
console.log(response.orderID, response.status);
```
### Python
```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},
order_type=OrderType.GTC,
)
print(response["orderID"], response["status"])
```
## Core Pattern: Read Orderbook
### TypeScript
```typescript
// No auth needed
const readClient = new ClobClient("https://clob.polymarket.com", 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)
book = read_client.get_order_book("TOKEN_ID")
mid = read_client.get_midpoint("TOKEN_ID")
spread = read_client.get_spread("TOKEN_ID")
```
## Core Pattern: WebSocket Subscribe
```typescript
const ws = new WebSocket("wss://ws-subscriptions-clob.polymarket.com/ws/market");
ws.onopen = () => {
ws.send(JSON.stringify({
type: "market",
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)
+206
View File
@@ -0,0 +1,206 @@
# 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.
## L1 Authentication (Private Key)
L1 proves wallet ownership via EIP-712 signature. Used to create or derive API credentials.
### EIP-712 Domain
```typescript
const domain = {
name: "ClobAuthDomain",
version: "1",
chainId: 137,
};
const types = {
ClobAuth: [
{ name: "address", type: "address" },
{ name: "timestamp", type: "string" },
{ name: "nonce", type: "uint256" },
{ 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",
};
```
### L1 Headers
| Header | Description |
|--------|-------------|
| `POLY_ADDRESS` | Polygon signer address |
| `POLY_SIGNATURE` | CLOB EIP-712 signature |
| `POLY_TIMESTAMP` | Current UNIX timestamp |
| `POLY_NONCE` | Nonce (default: 0) |
### Create / Derive Credentials
```typescript
// TypeScript
const client = new ClobClient("https://clob.polymarket.com", 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)
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)
## L2 Authentication (API Key)
L2 uses HMAC-SHA256 signatures from the API credentials. Required for all `/v1/trade/*` endpoints.
### L2 Headers (all 5 required)
| 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 |
### Initialize Trading Client
```typescript
// TypeScript
const client = new ClobClient(
"https://clob.polymarket.com",
137,
signer,
apiCreds, // { apiKey, secret, passphrase }
2, // signatureType
funderAddress // proxy wallet address
);
```
```python
# Python
client = ClobClient(
host="https://clob.polymarket.com",
chain_id=137,
key=pk,
creds=api_creds,
signature_type=2,
funder=funder_address,
)
```
## Signature Types
| 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. |
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.
## Builder Headers
Builder authentication is separate from L1/L2. Used for order attribution and relayer access.
### 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
```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!,
};
const builderConfig = new BuilderConfig({ localBuilderCreds: builderCreds });
const client = new ClobClient(
"https://clob.polymarket.com",
137,
signer,
apiCreds,
2,
funderAddress,
undefined,
false,
builderConfig
);
// Orders automatically include builder headers
```
```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"],
)
)
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,
)
```
### Remote Signing
Keep builder credentials on a separate server. Client points to your signing endpoint:
```typescript
// TypeScript client
const builderConfig = new BuilderConfig({
remoteBuilderConfig: { url: "https://your-server.com/sign" },
});
```
```python
# Python client
from py_builder_signing_sdk import BuilderConfig, RemoteBuilderConfig
builder_config = BuilderConfig(
remote_builder_config=RemoteBuilderConfig(url="https://your-server.com/sign")
)
```
Your server receives `{ method, path, body }` and returns the 4 `POLY_BUILDER_*` headers.
## 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
Lost credentials + lost nonce = create fresh credentials. Save your nonce.
+137
View File
@@ -0,0 +1,137 @@
# Bridge
Polymarket uses **USDC.e** (Bridged USDC) on Polygon as collateral. The Bridge API handles deposits from and withdrawals to multiple chains.
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}`
```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:
| Address | Use For |
|---------|---------|
| `evm` | Ethereum, Arbitrum, Base, Optimism, and other 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.
## 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}`
```bash
# Create withdrawal addresses
curl -X POST https://bridge.polymarket.com/withdraw \
-H "Content-Type: application/json" \
-d '{
"address": "0xYOUR_POLYMARKET_WALLET",
"toChainId": "1",
"toTokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"recipientAddr": "0xDESTINATION_ADDRESS"
}'
```
**Do not pre-generate withdrawal addresses.** Generate them only when ready to execute.
## 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
```
## Status Tracking
```bash
# Use the deposit address (not your wallet address)
curl https://bridge.polymarket.com/status/0xDEPOSIT_ADDRESS
```
### Transaction 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 |
Poll every 1030 seconds until `COMPLETED` or `FAILED`.
### Response
```json
{
"transactions": [{
"fromChainId": "1",
"fromTokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"fromAmountBaseUnit": "1000000000",
"toChainId": "137",
"toTokenAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"status": "COMPLETED",
"txHash": "0x...",
"createdTimeMs": 1697875200000
}]
}
```
Empty `transactions` array = no deposits detected yet.
## 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**.
## 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
+180
View File
@@ -0,0 +1,180 @@
# CTF Operations
The **Conditional Token Framework (CTF)** creates ERC1155 tokens for market outcomes. Three core operations: split, merge, redeem.
## Token Model
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 |
Every Yes/No pair is backed by exactly $1.00 USDC.e locked in the CTF contract.
## Split
Convert USDC.e into a full set of outcome tokens.
```
$100 USDC.e → 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`)
### 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 |
## Merge
Convert a full set of outcome tokens back to USDC.e. Inverse of split.
```
100 Yes tokens + 100 No tokens → $100 USDC.e
```
### 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.
## Redeem
Exchange winning tokens for USDC.e after market resolution.
```
Market resolves YES:
100 Yes tokens → $100 USDC.e
100 No tokens → $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 |
| `parentCollectionId` | bytes32 | `0x0000...0000` |
| `conditionId` | bytes32 | Market's condition ID |
| `indexSets` | uint[] | `[1, 2]` — redeems both (only winner pays) |
Redemption burns your **entire** token balance for the condition — no amount parameter. No deadline — winning tokens are always redeemable.
### Payout Vectors
| Outcome | Payout Vector | Redemption |
|---------|---------------|------------|
| Yes wins | `[1, 0]` | Yes = $1, No = $0 |
| No wins | `[0, 1]` | Yes = $0, No = $1 |
## Contract Addresses
| 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 |
| Neg Risk Adapter | `0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296` | Neg risk conversions |
## 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 |
| 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` |
## 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 |
### Identifying
```json
{
"enableNegRisk": true,
"negRiskAugmented": 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
## Token ID Computation
Token IDs are computed onchain in three steps:
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 practice, get token IDs from the Markets API `tokens` array. Manual computation only needed for direct contract interaction.
+236
View File
@@ -0,0 +1,236 @@
# 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.
Requires **Builder Program** membership. You need Builder API credentials.
## 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
## What's Covered
| 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 |
## Installation
```bash
# TypeScript
npm install @polymarket/builder-relayer-client @polymarket/builder-signing-sdk
# Python
pip install py-builder-relayer-client py-builder-signing-sdk
```
## Client Setup
### TypeScript (Local Signing)
```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({
account,
chain: polygon,
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(
"https://relayer-v2.polymarket.com/",
137,
wallet,
builderConfig
);
```
### Python (Local Signing)
```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")
)
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 |
```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
);
// 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"))
```
## Executing Transactions
```typescript
interface Transaction {
to: string; // Target contract address
data: string; // Encoded function call
value: string; // POL to send (usually "0")
}
const response = await client.execute(transactions, "Description");
const result = await response.wait();
```
### Token Approval Example
```typescript
import { encodeFunctionData, maxUint256 } from "viem";
const USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
const CTF = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045";
const approveTx = {
to: USDC,
data: encodeFunctionData({
abi: [{
name: "approve", type: "function",
inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }],
outputs: [{ type: "bool" }],
}],
functionName: "approve",
args: [CTF, maxUint256],
}),
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();
```
## Transaction States
| State | Terminal | Description |
|-------|----------|-------------|
| `STATE_NEW` | No | Received by relayer |
| `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
## 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)
+216
View File
@@ -0,0 +1,216 @@
# 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).
## Gamma API
Base URL: `https://gamma-api.polymarket.com` — no auth required.
### Events Endpoint
```bash
# All active events
GET https://gamma-api.polymarket.com/events?active=true&closed=false&limit=100
# By slug (from polymarket.com/event/{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
# 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
```
### Markets Endpoint
```bash
# By slug
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` | 1500 (default: 20) |
| `offset` | Pagination offset |
### Pagination
```bash
# Page 1
GET https://gamma-api.polymarket.com/events?active=true&closed=false&limit=50&offset=0
# Page 2
GET https://gamma-api.polymarket.com/events?active=true&closed=false&limit=50&offset=50
```
Response includes `has_more: true/false`. Increment offset by limit until `has_more` is `false`.
### Tags & Sports
```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.
## CLOB Orderbook
Base URL: `https://clob.polymarket.com` — no auth for read endpoints.
### Get Orderbook
```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 }
```
```python
# Python
client = ClobClient("https://clob.polymarket.com", chain_id=137)
book = client.get_order_book("TOKEN_ID")
```
```bash
# REST
curl "https://clob.polymarket.com/book?token_id=TOKEN_ID"
```
### Prices
```typescript
const buyPrice = await client.getPrice("TOKEN_ID", "BUY"); // best ask
const sellPrice = await client.getPrice("TOKEN_ID", "SELL"); // best bid
```
```bash
curl "https://clob.polymarket.com/price?token_id=TOKEN_ID&side=BUY"
```
### Midpoint
```typescript
const mid = await client.getMidpoint("TOKEN_ID"); // { mid: "0.50" }
```
If bid-ask spread > $0.10, Polymarket UI shows last traded price instead of midpoint.
### Spread
```typescript
const spread = await client.getSpread("TOKEN_ID"); // { spread: "0.04" }
```
### Last Trade Price
```typescript
const last = await client.getLastTradePrice("TOKEN_ID"); // { price, side }
```
### 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 }
```
| Interval | Description |
|----------|-------------|
| `1h` | Last hour |
| `6h` | Last 6 hours |
| `1d` | Last day |
| `1w` | Last week |
| `1m` | Last month |
| `max` | All available |
Use `startTs`/`endTs` for absolute ranges (mutually exclusive with `interval`).
### Estimate Fill Price
Walk the orderbook to estimate slippage for a given order size:
```typescript
const price = await client.calculateMarketPrice(
"TOKEN_ID", Side.BUY, 500, OrderType.FOK
);
```
### 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 },
]);
```
## 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 |
```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 } }"}'
```
## 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
+303
View File
@@ -0,0 +1,303 @@
# Order Patterns
All orders on Polymarket are expressed as limit orders. Market orders are limit orders with a marketable price that execute immediately.
## 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 |
## Tick Sizes
Price must conform to the market's tick size or the order is rejected.
| 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.
## Limit Order (GTC)
```typescript
// TypeScript — one-step
const response = await client.createAndPostOrder(
{ tokenID: "TOKEN_ID", price: 0.50, size: 10, side: Side.BUY },
{ tickSize: "0.01", negRisk: false },
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},
order_type=OrderType.GTC,
)
```
### Two-step (sign then submit)
```typescript
// TypeScript
const signedOrder = await client.createOrder(
{ tokenID: "TOKEN_ID", price: 0.50, size: 10, side: Side.BUY },
{ 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)
```
## Market Order (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
```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 },
{ tickSize: "0.01", negRisk: false },
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.
**Effective lifetime of N seconds: `now + 60 + N`**
```typescript
// TypeScript — expire in 1 hour
const expiration = Math.floor(Date.now() / 1000) + 60 + 3600;
const response = await client.createAndPostOrder(
{ tokenID: "TOKEN_ID", price: 0.50, size: 10, side: Side.BUY, expiration },
{ tickSize: "0.01", negRisk: false },
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).
```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
## Batch Orders
Up to **15 orders** in a single request.
```typescript
// TypeScript
const orders: PostOrdersArgs[] = [
{
order: await client.createOrder(
{ tokenID: "TOKEN_ID", price: 0.48, side: Side.BUY, size: 500 },
{ 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 }
),
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,
),
])
```
## 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",
});
```
```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**.
```typescript
// TypeScript
let heartbeatId = "";
setInterval(async () => {
const resp = await client.postHeartbeat(heartbeatId);
heartbeatId = resp.heartbeat_id;
}, 5000);
```
```python
# Python
import time
heartbeat_id = ""
while True:
resp = client.post_heartbeat(heartbeat_id)
heartbeat_id = resp["heartbeat_id"]
time.sleep(5)
```
- 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
```
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 |
## 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.
Max order size = `balance - sum(openOrderSize - filledAmount)`
## Sports Markets
- Outstanding limit orders auto-cancelled when game begins
- Marketable orders have 3-second placement delay
- Game start times can shift — monitor accordingly
+228
View File
@@ -0,0 +1,228 @@
# WebSocket
Three channels for real-time data. Market and sports channels are public; user channel requires API credentials.
## Channels
| Channel | Endpoint | Auth |
|---------|----------|------|
| Market | `wss://ws-subscriptions-clob.polymarket.com/ws/market` | No |
| User | `wss://ws-subscriptions-clob.polymarket.com/ws/user` | Yes |
| Sports | `wss://sports-api.polymarket.com/ws` | No |
## Market Channel
Public. Subscribes by **asset IDs** (token IDs).
### Subscribe
```json
{
"assets_ids": ["TOKEN_ID_1", "TOKEN_ID_2"],
"type": "market",
"custom_feature_enabled": true
}
```
Set `custom_feature_enabled: true` to enable `best_bid_ask`, `new_market`, and `market_resolved` events.
### Event Types
| Event | Trigger | Key Fields |
|-------|---------|------------|
| `book` | On subscribe + when trade affects book | `bids[]`, `asks[]`, `hash`, `timestamp` |
| `price_change` | Order placed or cancelled | `price_changes[]` with `price`, `size`, `side`, `best_bid`, `best_ask` |
| `last_trade_price` | Trade executed | `price`, `side`, `size`, `fee_rate_bps` |
| `tick_size_change` | Price hits >0.96 or <0.04 | `old_tick_size`, `new_tick_size` |
| `best_bid_ask` | Top-of-book changes | `best_bid`, `best_ask`, `spread` |
| `new_market` | Market created | `question`, `assets_ids`, `outcomes` |
| `market_resolved` | Market resolved | `winning_asset_id`, `winning_outcome` |
Events requiring `custom_feature_enabled: true`: `best_bid_ask`, `new_market`, `market_resolved`.
**`tick_size_change` is critical for bots** — if tick size changes and you use the old one, orders are rejected.
A `price_change` with `size: "0"` means the price level was removed from the book.
### Example Messages
```json
// book
{
"event_type": "book",
"asset_id": "TOKEN_ID",
"market": "0xCONDITION_ID",
"bids": [{"price": ".48", "size": "30"}],
"asks": [{"price": ".52", "size": "25"}],
"timestamp": "123456789000",
"hash": "0x..."
}
```
```json
// price_change
{
"event_type": "price_change",
"market": "0xCONDITION_ID",
"price_changes": [{
"asset_id": "TOKEN_ID",
"price": "0.5",
"size": "200",
"side": "BUY",
"hash": "...",
"best_bid": "0.5",
"best_ask": "1"
}],
"timestamp": "..."
}
```
## User Channel
Authenticated. Subscribes by **condition IDs** (market IDs), not asset IDs. The `markets` field is optional — omit it to receive events for all markets.
### Subscribe
```json
{
"auth": {
"apiKey": "your-api-key",
"secret": "your-api-secret",
"passphrase": "your-passphrase"
},
"markets": ["0xCONDITION_ID"],
"type": "user"
}
```
### Event Types
| Event | Trigger |
|-------|---------|
| `trade` | Trade lifecycle: MATCHED, MINED, CONFIRMED, RETRYING, FAILED |
| `order` | Order lifecycle: PLACEMENT, UPDATE, CANCELLATION |
### Trade Message
```json
{
"event_type": "trade",
"id": "trade-uuid",
"market": "0xCONDITION_ID",
"asset_id": "TOKEN_ID",
"side": "BUY",
"size": "10",
"price": "0.57",
"status": "MATCHED",
"maker_orders": [{ "order_id": "0x...", "matched_amount": "10", "price": "0.57" }],
"type": "TRADE"
}
```
### Order Message
```json
{
"event_type": "order",
"id": "0xORDER_ID",
"market": "0xCONDITION_ID",
"asset_id": "TOKEN_ID",
"side": "SELL",
"price": "0.57",
"original_size": "10",
"size_matched": "0",
"type": "PLACEMENT"
}
```
Order types: `PLACEMENT`, `UPDATE` (partial fill), `CANCELLATION`.
## Sports Channel
No subscription message needed. Connect and receive all active sports data.
```json
// sport_result
{ "type": "sport_result", ... } // Live scores, periods, status
```
## Dynamic Subscribe / Unsubscribe
Modify subscriptions without reconnecting:
```json
// Market channel — subscribe to more
{ "assets_ids": ["NEW_TOKEN_ID"], "operation": "subscribe", "custom_feature_enabled": true }
// Market channel — unsubscribe
{ "assets_ids": ["OLD_TOKEN_ID"], "operation": "unsubscribe" }
// User channel — subscribe to more markets
{ "markets": ["0xNEW_CONDITION_ID"], "operation": "subscribe" }
```
## Heartbeat
### Market & User Channels
Send `PING` every **10 seconds**. Server responds with `PONG`.
```typescript
const ws = new WebSocket("wss://ws-subscriptions-clob.polymarket.com/ws/market");
ws.onopen = () => {
// Subscribe...
setInterval(() => ws.send("PING"), 10_000);
};
ws.onmessage = (event) => {
if (event.data === "PONG") return;
const msg = JSON.parse(event.data);
// handle msg.event_type
};
```
### Sports Channel
Server sends `ping` every 5 seconds. Respond with `pong` within 10 seconds or connection closes.
## Full TypeScript Example
```typescript
const ws = new WebSocket("wss://ws-subscriptions-clob.polymarket.com/ws/market");
ws.onopen = () => {
ws.send(JSON.stringify({
type: "market",
assets_ids: ["TOKEN_ID"],
custom_feature_enabled: true,
}));
setInterval(() => ws.send("PING"), 10_000);
};
ws.onmessage = (event) => {
if (event.data === "PONG") return;
const msg = JSON.parse(event.data);
switch (msg.event_type) {
case "book":
console.log("Book snapshot:", msg.bids.length, "bids", msg.asks.length, "asks");
break;
case "price_change":
for (const pc of msg.price_changes) {
console.log(`${pc.side} ${pc.size}@${pc.price} (best: ${pc.best_bid}/${pc.best_ask})`);
}
break;
case "last_trade_price":
console.log(`Trade: ${msg.side} ${msg.size}@${msg.price}`);
break;
case "tick_size_change":
console.log(`Tick: ${msg.old_tick_size}${msg.new_tick_size}`);
break;
}
};
```
## Troubleshooting
- **Connection closes immediately**: send subscription message right after open
- **Drops after ~10s**: you're not sending PING heartbeats
- **No messages**: verify asset IDs are correct and markets are active
- **Auth failed (user channel)**: check API credentials haven't expired