web3-polymarket
$
npx mdskill add elophanto/EloPhanto/web3-polymarketExecute prediction market trades on Polygon with advanced order types.
- Enables agents to place GTC, GTD, FOK, and FAK orders instantly.
- Integrates with Polymarket CLOB, Gamma API, and Polygon RPC.
- Uses vault-stored private keys and HMAC-SHA256 authentication.
- Delivers real-time market data via WebSocket streams.
SKILL.md
.github/skills/web3-polymarketView on GitHub ↗
---
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
> **Source:** Official Polymarket skill from [Polymarket/agent-skills](https://github.com/Polymarket/agent-skills). Keep in sync with upstream. The "EloPhanto Setup" section below is local — everything else mirrors upstream.
## EloPhanto Setup
**Triggers:** "polymarket", "prediction market", "place a bet", "polygon", "CLOB", "trading bot for polymarket".
### 1. Install the Python SDK on first use
```bash
pip install py-clob-client
```
(Run via `shell_execute`. Not in `pyproject.toml` by default — only installed when this skill is actually used.)
### 2. Store credentials in the vault (one-time)
```
vault_set polymarket_private_key VALUE # Polygon EOA private key (the funding wallet)
vault_set polymarket_funder_address VALUE # Proxy/safe address from polymarket.com/settings (only if signature_type != 0)
vault_set polymarket_signature_type 2 # 0=EOA, 1=POLY_PROXY, 2=GNOSIS_SAFE (most common = 2)
```
### 3. Use credentials in Python via `vault_lookup`
```python
from py_clob_client.client import ClobClient
pk = vault_lookup("polymarket_private_key")
funder = vault_lookup("polymarket_funder_address")
sig_type = int(vault_lookup("polymarket_signature_type") or "2")
temp = ClobClient("https://clob.polymarket.com", key=pk, chain_id=137)
creds = temp.create_or_derive_api_creds()
client = ClobClient(
"https://clob.polymarket.com",
key=pk, chain_id=137,
creds=creds,
signature_type=sig_type,
funder=funder,
)
```
### 3a. Auto-detect which signature_type holds the collateral
The same `polymarket_private_key` can have funds across **multiple
proxy types** (EOA, POLY_PROXY, GNOSIS_SAFE). Polymarket's web UI
shows whichever wallet you're toggled to; users routinely deposit
into one (e.g. POLY_PROXY) while their vault config points at
another (e.g. GNOSIS_SAFE). Result: SDK reports `$0 USDC` and the
order fails with "insufficient balance" or `order_version_mismatch`
for reasons that look like coding errors.
**Always probe all three before placing the first order of a
session** and use whichever one is funded:
```python
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import BalanceAllowanceParams, AssetType
best_sig_type = None
best_balance_usdc = 0.0
for sig_type in (0, 1, 2):
try:
c = ClobClient(
"https://clob.polymarket.com",
key=pk, chain_id=137,
signature_type=sig_type,
funder=funder,
)
c.set_api_creds(c.create_or_derive_api_creds())
bal = c.get_balance_allowance(
BalanceAllowanceParams(
asset_type=AssetType.COLLATERAL, signature_type=sig_type
)
)
usdc = int(bal.get("balance", 0)) / 1_000_000
print(f"sig_type={sig_type}: ${usdc:.2f} USDC")
if usdc > best_balance_usdc:
best_balance_usdc, best_sig_type = usdc, sig_type
except Exception as e:
print(f"sig_type={sig_type}: probe failed ({e!r})")
if best_sig_type is None or best_balance_usdc == 0:
raise SystemExit("No funded Polymarket wallet found for this key.")
# Use the funded sig_type for the rest of the session.
client = ClobClient(
"https://clob.polymarket.com",
key=pk, chain_id=137,
signature_type=best_sig_type,
funder=funder,
)
client.set_api_creds(client.create_or_derive_api_creds())
```
If the detected `best_sig_type` differs from the vault setting,
**update the vault** so future sessions don't re-probe:
`vault_set polymarket_signature_type <N>`.
### 4. Place orders ONLY via the API. Never via the browser.
**Hard rule:** all Polymarket order placement must go through
`py-clob-client`. **Never** drive `polymarket.com` via the browser
tools to place a trade. The web UI uses Privy/embedded-wallet flows
that aren't compatible with the funder wallet stored in the vault —
mis-clicks place real orders, and the agent has no way to verify the
order before it submits. If the SDK fails, **stop and report the
exact error**; do not switch to the GUI as a fallback.
### 5. Discover tick_size and neg_risk dynamically (don't hardcode)
`create_order` requires both options. Both are per-market and the
SDK / on-chain values are authoritative — guessing a wrong tick_size
("0.01" vs "0.001") rejects the order with a confusing error.
```python
# Fetch directly from the CLOB
tick_size = client.get_tick_size(token_id) # "0.001" / "0.01" / "0.1"
neg_risk = client.get_neg_risk(token_id) # bool
signed = client.create_order(
OrderArgs(token_id=token_id, price=price, size=size, side=BUY),
options={"tick_size": tick_size, "neg_risk": neg_risk}, # ← keyword arg, dict
)
resp = client.post_order(signed, OrderType.GTC)
```
If `get_neg_risk` isn't available in your installed py-clob-client
version, read `negRisk` from the market metadata via gamma-api:
```python
import requests
m = requests.get(
"https://gamma-api.polymarket.com/events",
params={"slug": EVENT_SLUG}, timeout=20,
).json()[0]["markets"]
neg_risk = next(mk for mk in m if mk["clobTokenIds"] and token_id in mk["clobTokenIds"])["negRisk"]
```
The `options` parameter is **keyword-only in newer SDK versions** —
passing it positionally raises `TypeError`. Always use `options=`.
### 6. Safety rails
- **Always confirm with the owner before placing real-money orders.** Treat trade execution as `DESTRUCTIVE` permission level — surface the order params (token, side, price, size, USDC cost) and wait for explicit approval.
- Read-only operations (orderbook, market data, positions) need no approval.
- Polymarket trades USDC.e on Polygon. Make sure the funder wallet has USDC.e and a small amount of POL for gas (only if `signature_type=0`; gasless via Gnosis Safe doesn't need POL).
- Never log or echo `polymarket_private_key`.
---
## 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)
## Verify
- A real RPC/SDK call was issued (mainnet, devnet, or local validator) and the response payload is captured in the transcript, not just paraphrased
- Every transaction was simulated (`simulateTransaction` or equivalent) before any signing/sending step; simulation logs are attached
- For any signed/sent transaction, the resulting signature is recorded and confirmed on chain (status returned by `getSignatureStatuses` or an explorer URL)
- Slippage, priority-fee, and compute-unit limits were set explicitly with concrete numeric values, not left to library defaults
- Account addresses, mints, and program IDs used in the run match the documented web3-polymarket addresses for the targeted cluster (no mainnet/devnet mix-up)
- Failure path was exercised at least once (insufficient balance, stale oracle, expired blockhash, etc.) and the agent's error handling produced a human-readable message
More from elophanto/EloPhanto
- 12-principles-of-animationAudit animation code against Disney's 12 principles adapted for web. Use when reviewing motion, implementing animations, or checking animation quality. Outputs file:line findings.
- accessibility-auditingAudit interfaces against WCAG 2.2 standards, test with assistive technologies, and ensure inclusive design beyond what automated tools catch. Adapted from msitarzewski/agency-agents.
- agency-phase-0-discoveryIntelligence and discovery phase — validate opportunity before committing resources. Adapted from msitarzewski/agency-agents.
- agency-phase-1-strategyStrategy and architecture phase — define what to build, how to structure it, and what success looks like. Adapted from msitarzewski/agency-agents.
- agency-phase-2-foundationFoundation and scaffolding phase — build technical and operational foundation before feature development. Adapted from msitarzewski/agency-agents.
- agency-phase-3-buildBuild and iterate phase — implement all features through continuous Dev-QA loops with orchestrated multi-agent sprints. Adapted from msitarzewski/agency-agents.
- agency-phase-4-hardeningQuality and hardening phase — the final quality gauntlet proving production readiness with evidence. Adapted from msitarzewski/agency-agents.
- agency-phase-5-launchLaunch and growth phase — coordinate go-to-market execution across all channels for maximum impact. Adapted from msitarzewski/agency-agents.
- agency-phase-6-operateOperate and evolve phase — sustained operations with continuous improvement for live products. Adapted from msitarzewski/agency-agents.
- agency-strategyNEXUS multi-agent orchestration strategy — the complete operational playbook for coordinating specialized AI agents across project phases. Adapted from msitarzewski/agency-agents.