# Polymarket trading & authentication Authenticated CLOB access is two-layered. `TradingClient` (in `scripts/polymarket.py`) wraps the official **v2** SDK (`py_clob_client_v2`), which implements both layers correctly. Read this when an order behaves unexpectedly or you need to choose a signature type. ```bash pip install py_clob_client_v2 ``` ## L1 — wallet / EIP-712 Your wallet private key signs an EIP-712 message under the `ClobAuthDomain` (name `ClobAuthDomain`, version `1`, chainId `137` Polygon). L1 is used to: - create API credentials (`POST /auth/api-key`) - derive existing credentials (`GET /auth/derive-api-key`) - sign each order locally before it is posted The L1 request headers are `POLY_ADDRESS`, `POLY_SIGNATURE`, `POLY_TIMESTAMP`, `POLY_NONCE`. You never send the private key anywhere — only signatures. ## L2 — API key / HMAC Credential creation returns `apiKey`, `secret`, `passphrase`. Every subsequent trading request (post/cancel/query orders, balances) is authenticated with an HMAC-SHA256 signature over the request, sent as headers `POLY_ADDRESS`, `POLY_SIGNATURE`, `POLY_TIMESTAMP`, `POLY_API_KEY`, `POLY_PASSPHRASE`. `TradingClient` calls `create_or_derive_api_key()` on construction, so the first run derives (or creates) and caches creds in memory. To reuse fixed creds across runs, set `CLOB_API_KEY`, `CLOB_SECRET`, `CLOB_PASSPHRASE`. ## Signature types (how your funds are held) Pass `signature_type` to `TradingClient(signature_type=...)`: - `0` — EOA: you trade directly from the signing wallet. - `1` — Polymarket proxy (older email/magic login). Set `funder=` / `POLY_PROXY_ADDRESS` to the proxy address that holds the USDC. - `2` — Gnosis-safe proxy (browser-wallet signup). Also needs `funder=`. - `3` — newer Polymarket embedded-wallet accounts (current email/Google signups). The signer key (from "Export Private Key" in Settings → 账号 → 私钥) resolves to a Magic EOA, while the funded **trading account** is a *separate* address shown in Settings → 个人资料 → 地址 (even though it's labelled "for API use only"). Under type 3 the SDK derives that account from the signer automatically, so `get_balance_allowance()` shows the balance with no `funder` needed. Undocumented gotcha that wastes hours: types 0/1/2 will all silently return `balance: 0` for these newer accounts because they derive the wrong proxy. If auth succeeds (creds + correct signer address) but balance is 0 under 1/2, **try `signature_type=3`**. Confirm you've got the right type when `balance` matches your on-screen 现金 and the three exchange `allowances` are large (≈2^256-1). If you signed up on polymarket.com with a browser wallet, you are almost always type `2`; older email/magic is `1`, newer email/Google is often `3`. Trading from a raw key you funded directly is type `0`. Getting this wrong yields "not enough balance/allowance" errors even when funds are visible in the UI. ## Order types - `GTC` — good-till-cancelled limit order (rests on the book). - `GTD` — good-till-date; supply `expiration` (unix seconds). - `FOK` — fill-or-kill (used for market orders by default). - `FAK` — fill-and-kill (immediate, partial allowed, remainder cancelled). Prices are in USDC per share, 0–1. Size is in shares (for limit orders) or USDC amount (for `create_market_order`). Tick size and min order size vary by market: check `clob.tick_size(token_id)` first; orders off-tick are rejected. ## Allowances Before the first trade the exchange contracts need USDC (and CTF) allowances set on-chain. The official client exposes `update_balance_allowance()` / `get_balance_allowance()`; the Polymarket web UI sets these automatically on first deposit. If posting fails with an allowance error and you funded via the website, you likely have the wrong `signature_type`/`funder` (see above) rather than a missing allowance. ## Safety These are real, hard-to-reverse financial actions. Always confirm token_id, side, price, and size with the user before posting, and never place an order the user did not explicitly authorize. Do not log the private key.