add skills
This commit is contained in:
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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.
|
||||||
@@ -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 10–30 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
|
||||||
@@ -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
@@ -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
@@ -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` | 1–500 (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
|
||||||
@@ -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
@@ -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
|
||||||
Reference in New Issue
Block a user