npx skills add ...
npx skills add starchild-ai-agent/official-skills --skill polymarket
Trade on Polymarket prediction markets (CLOB V2) from a Privy EOA wallet. Search markets, place/cancel orders, manage positions. No private key handling.
npx skills add starchild-ai-agent/official-skills --skill polymarket
Script-first. Every workflow is one bash call + at most one wallet_sign_typed_data (which can't be scripted because it's a wallet RPC). Verified live: see Changelog v5.1.0.
All scripts live in skills/polymarket/scripts/ and assume working dir is the skill folder.
CLOB V2 settles in pUSD (an ERC-20 USDC wrapper), NOT raw USDC.e. Before the first order, the EOA must:
CollateralOnramp.wrap().approve(pUSD, spender, MAX) for the 3 V2 exchange spenders.setApprovalForAll(CTF, spender, true) for the same 3 spenders (needed for SELL/redemption).scripts/setup.py does all of this and is idempotent: it reads on-chain state and skips anything already done. Gas is sponsored by the Privy/Alchemy paymaster — user pays 0 MATIC.
| Script | Purpose | Tool calls |
|---|---|---|
setup.py | One-time wrap + approvals (idempotent) | 0–8 wallet_transfer |
search.py | Find events/markets, returns token_ids + live prices | 0 |
status.py | Balance + positions + open orders + recent trades | 0 |
prepare_order.py | Fetch orderbook + build EIP-712 payload | 0 |
post_order.py | Submit signed order, verify fill | 0 |
cancel.py | Cancel one order (--id) or all (--all) | 0 |
close_positions.py | Build SELL orders for all positions | 0 |
auth.py | Check / derive CLOB API key from wallet | 0–1 sign |
trump, btc, ceasefire), not full literal questions — long queries often return empty.outcomes[i].token_id (YES = index 0, NO = index 1) and current price.Every binary market has two complementary tokens: YES + NO ≈ $1.00.
| Bet | Action | Use | Buy price |
|---|---|---|---|
| Event happens | BUY YES | outcomes[0].token_id | YES price |
| Event won't happen | BUY NO | outcomes[1].token_id | NO price |
Always check the book for the token you intend to buy:
best_askbest_bidprice × size ≥ 1.0)prepare_order.py (usually $0.01)signatureType: always 0 (EOA) for Privy walletsV2 wire format is strict — post_order.py already handles this, but if you ever build a payload by hand:
salt must be int (not string)taker / nonce / feeRateBps (V1 fields, removed in V2)metadata and builder are bytes32 zeros (0x + 64 zeros)CLOB credentials (POLY_API_KEY / POLY_SECRET / POLY_PASSPHRASE) are derived from a wallet signature once and persist. If status.py returns 401:
Invariants: TIMESTAMP and 0xWALLET in --save must match the most recent --prepare. If 401 persists, rerun the full prepare→sign→save flow with a fresh timestamp.
CLOB API is geo-blocked from US IPs. scripts/common.py transparently routes through sc-vpn.internal:8080 and caches the fastest region. No agent action needed. Override with POLY_VPN_REGION=ar or disable with POLY_DISABLE_VPN=true.
| Error | Cause | Fix |
|---|---|---|
Invalid order payload | Wrong V2 wire format (string salt / extra V1 fields) | Use post_order.py (it sends the right shape) |
invalid amount for a marketable BUY order ($X), min size: $1 | Order value < $1 | Increase price × size to ≥ $1 |
not enough balance / 0 buying power | pUSD not wrapped yet, OR signature_type mismatch in cache | setup.py --all 10, then status.py |
L2_BALANCE_TOO_LOW | No pUSD in EOA | Wrap more: setup.py --wrap N |
order_version_mismatch | Old V1 signing schema | Re-run prepare_order.py (uses V2 domain version=2) |
| 401 / Invalid API key | Stale CLOB credentials | auth.py refresh flow |
| 403 geo-block | VPN unhealthy | Retry; if persists, set POLY_VPN_REGION to another region |
| Orderbook shows 0.01 / 0.99 | Looking at the wrong outcome's book | Use the token_id you actually plan to buy |
signatureType=0. Agent signs EIP-712 via wallet_sign_typed_data; no private key in agent context.wallet_transfer.0xC011a7E1...DFB), 6 decimals, 1:1 wrap of USDC.e.https://clob.polymarket.com (V2 backend, live since Apr 28 2026). L1 = wallet sig, L2 = HMAC with derived API key.scripts/common.py::save_env_var() now guards missing trailing newline in .env to prevent key concatenation; (2) scripts/close_positions.py migrated from V1 wire fields to CLOB V2 schema (timestamp/metadata/builder, domain version=2) aligned with prepare_order.py. Added "Known transient errors" note for occasional Privy/Alchemy paymaster HTTP 500 under rapid consecutive calls (retry after ~10s).setup.py for one-time wrap + approvals, end-to-end live-verified on CLOB V2 (BUY 0x43f20c67...20b653 → SELL 0x19f475e4...fedde1, 5 NO @ 0.989 → @ 0.988, ~$0.005 slippage). Supersedes the V1-era flow entirely.setup.py (idempotent wrap + approvals). Full SKILL rewrite for clarity. Live-verified: BUY 0x43f20c67...20b653, SELL 0x19f475e4...fedde1 (5 NO @ 0.989 → @ 0.988, ~$0.005 slippage).salt must be int; remove taker/nonce/feeRateBps.