CLOB V2 Migration · Tutorial

Migrate your bot to Polymarket CLOB V2 by talking to your coding agent.

A step-by-step guide built for Claude Code, Cursor, Codex, Aider, Cline, Continue.dev, Windsurf, GitHub Copilot Chat, or any coding agent. Pick your stack, the prompts adapt. Walk one step at a time, hit Next when your agent finishes, or flip to "View all" to see everything stacked.

Cutover: April 28, 2026 · ~11:00 UTC. ~1 hour of downtime. All open orders are wiped. The V1 SDK and any V1 raw-signed clients stop working after the window. Test now against https://clob-v2.polymarket.com; after cutover, V2 takes over https://clob.polymarket.com.
00 / Setup

Pick your stack and your view

This walkthrough is 14 prompts. You open a fresh chat in your coding agent of choice, you paste the first prompt, you let the agent work, you come back and hit Next. The prompts are written to be self-contained — an agent that has never seen your repo before can act on each one without prior context.

Pick your bot's stack to tailor the prompts. Pick your view: Guide mode shows one step at a time so you focus, View all dumps everything onto one page if you'd rather scan or print.

Your bot's stack
Prompts rewrite themselves to use your stack's idioms. Pick "I'm not sure" and Step 1 will detect it.
View
Guide mode = one step at a time, paginated. View all = current page as a single scroll.
01 / Walkthrough

14 prompts to a fully migrated bot

00
Open a fresh agent chat in your bot's repo
UNIVERSAL · No code yet

Start a brand-new chat session in your preferred coding agent so it begins with zero context. Open the agent inside the root directory of your Polymarket bot (Claude Code, Cursor, Aider, etc. all key off the working directory).

  • Don't reuse an old chat — stale context will confuse the migration.
  • Make sure your repo has a clean working tree and is on a feature branch (e.g. git checkout -b clob-v2-migration).
  • If your bot is in production, take it offline before Step 11. Steps 1–10 don't deploy anything, so they're safe on a live bot.
Outside your agent

Open your terminal or IDE, change into your bot's repo, and start a new chat in your coding agent (e.g. claude in the repo root, or open the project in Cursor and start a new conversation). Once the agent is up and pointed at your repo, click Next and copy Step 1.

i This step has no prompt to paste. Once your agent chat is open and pointed at your bot's repo, mark Done and continue.
Step 1 of 15
01
Audit the repo — tell me what to migrate
UNIVERSAL · Read-only

This prompt does no edits. It hands you back a numbered punch list of every site in the codebase that needs to change — SDK imports, constructor calls, order objects, USDC.e references, builder HMAC code, raw EIP-712 signing, hardcoded V1 contract addresses. Use the punch list to decide which downstream steps apply to your bot.

Paste into your agent · universal
You are helping me migrate my Polymarket trading bot from CLOB V1 to CLOB V2 ahead of the April 28, 2026 ~11:00 UTC cutover. Before any code changes, do a read-only audit of this repo and report back, in a single response, the following:

1. The primary language(s) used (TypeScript, Python, Go, Rust, etc.) and the package/build files in use (package.json, requirements.txt, pyproject.toml, go.mod, Cargo.toml, etc.).
2. Whether I use the Polymarket SDK or sign orders directly via raw HTTP/EIP-712. Look for imports of `@polymarket/clob-client`, `py-clob-client`, `@polymarket/builder-signing-sdk`, or any custom EIP-712 signing code.
3. Every import site and constructor call for `ClobClient` / equivalents — list file paths and line numbers.
4. Every place where order objects are constructed — look for `feeRateBps`, `nonce`, `taker`, `expiration`, `salt`, `tokenID`, `makerAmount`, `takerAmount`.
5. Every place I reference USDC.e by address or symbol.
6. Every place I read the `POLY_BUILDER_*` env vars or send `POLY_BUILDER_*` HTTP headers.
7. Any hardcoded references to the V1 Exchange contract `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` or Neg Risk Exchange `0xC5d563A36AE78145C45a50134d48A1215220f80a`.
8. WebSocket / streaming code paths, if any.
9. The base URL the client points at (e.g. `https://clob.polymarket.com`).
10. Any manual fee-calculation logic (variables like `feeBps`, `FEE_RATE`, formulas resembling `rate * p * (1 - p)`).

Do not edit anything yet. Output a numbered checklist of what needs to change, ordered by file. I'll then walk you through each migration step.
You are helping me migrate my Polymarket trading bot from CLOB V1 to CLOB V2 ahead of the April 28, 2026 ~11:00 UTC cutover. This is a TypeScript / JavaScript codebase. Before any code changes, do a read-only audit and report back, in a single response:

1. Confirm the package manager from the lockfile (npm / yarn / pnpm), and read package.json + tsconfig.json.
2. Every import site for `@polymarket/clob-client` and `@polymarket/builder-signing-sdk`. File paths and line numbers.
3. Every `new ClobClient(...)` constructor call. File, line, full argument signature.
4. Every place where order objects are built — search for `feeRateBps`, `nonce`, `taker`, and the `UserOrder` / `UserMarketOrder` types.
5. Every reference to USDC.e by address or symbol.
6. Every read of `POLY_BUILDER_*` env vars or send of `POLY_BUILDER_*` HTTP headers.
7. Hardcoded V1 contract addresses: `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` (CTF) or `0xC5d563A36AE78145C45a50134d48A1215220f80a` (NegRisk).
8. WebSocket / streaming code paths.
9. The base URL the client points at.
10. Any manual fee-calculation logic (`feeBps`, `FEE_RATE`, formulas like `rate * p * (1 - p)`).

Don't edit anything yet. Output a numbered checklist of what needs to change, ordered by file.
You are helping me migrate my Polymarket trading bot from CLOB V1 to CLOB V2 ahead of the April 28, 2026 ~11:00 UTC cutover. This is a Python codebase. Before any code changes, do a read-only audit and report back, in a single response:

1. Confirm dependency tool in use (pip + requirements.txt, poetry + pyproject.toml, pipenv + Pipfile) and the Python version. Note env tool (venv / poetry / pipenv / conda).
2. Every `from py_clob_client...` import site. File paths and line numbers.
3. Every `ClobClient(...)` instantiation. File, line, full argument signature.
4. Every place where order objects are built — search for `fee_rate_bps`, `nonce`, `taker`, and `OrderArgs` / `MarketOrderArgs` (or equivalent dataclasses).
5. Every reference to USDC.e by address or symbol.
6. Every read of `POLY_BUILDER_*` env vars or send of `POLY_BUILDER_*` HTTP headers.
7. Hardcoded V1 contract addresses: `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` (CTF) or `0xC5d563A36AE78145C45a50134d48A1215220f80a` (NegRisk).
8. WebSocket / streaming code (look for `websockets`, `websocket-client`, `aiohttp.ClientSession.ws_connect`).
9. The base URL the client points at.
10. Any manual fee-calculation logic.

Don't edit anything yet. Output a numbered checklist of what needs to change, ordered by file.
You are helping me migrate my Polymarket trading bot from CLOB V1 to CLOB V2 ahead of the April 28, 2026 ~11:00 UTC cutover. This bot does NOT use the official Polymarket SDK — it signs orders directly via EIP-712 and posts them over raw HTTP. Before any code changes, do a read-only audit and report back, in a single response:

1. The primary language(s) and signing library in use (e.g. ethers.js, eth_account, go-ethereum, ethers-rs, ethereumjs-util, web3j).
2. Every place that constructs the EIP-712 typed-data domain — search for "Polymarket CTF Exchange", `verifyingContract`, `chainId: 137`, `version: "1"`.
3. Every place that defines or encodes the Order EIP-712 type / struct definition.
4. Every place that builds an order payload — search for `feeRateBps`, `nonce`, `taker`, `salt`, `tokenId`, `makerAmount`, `takerAmount`.
5. Every place that constructs the POST /order request body and the auth headers.
6. Every `POLY_BUILDER_*` env var read or HTTP header sent.
7. Hardcoded V1 contract addresses: `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` (CTF) or `0xC5d563A36AE78145C45a50134d48A1215220f80a` (NegRisk).
8. Every reference to USDC.e by address or symbol.
9. WebSocket / streaming code paths.
10. The base URL the client points at.

Don't edit anything yet. Output a numbered checklist of what needs to change, ordered by file.
i After pasting: read the agent's response. It may ask follow-up questions to scope the audit, or report files it can't access. Once it has produced the punch list, mark Done and continue.
Step 2 of 15
02
Make the CLOB host configurable, point at the V2 staging endpoint
UNIVERSAL · Small config diff

Move the CLOB host into an env var so you can flip from staging (clob-v2.polymarket.com) to production (clob.polymarket.com) without re-editing code. After cutover, V2 takes over the production URL — you'll just change the env var, not the code.

Paste into your agent · universal
Update my bot to point its CLOB base URL at the V2 staging endpoint so I can test before the April 28 cutover:

  https://clob-v2.polymarket.com

Make this configurable via env var (default: `POLYMARKET_CLOB_HOST`) defaulting to that test URL. After cutover, V2 takes over the production URL `https://clob.polymarket.com`, so I want to flip the env var rather than re-edit code.

Show me the diff and explain where the host is consumed. If there are multiple places that hardcode the host, route them all through the new env var.
Update my bot to point its CLOB base URL at the V2 staging endpoint so I can test before the April 28 cutover:

  https://clob-v2.polymarket.com

Make it configurable via `process.env.POLYMARKET_CLOB_HOST`, defaulting to that staging URL. After cutover, V2 takes over the production URL `https://clob.polymarket.com`, so I want to flip the env var rather than re-edit code.

Show me the diff. If multiple files hardcode the host, route them all through the same config helper / module.
Update my bot to point its CLOB base URL at the V2 staging endpoint so I can test before the April 28 cutover:

  https://clob-v2.polymarket.com

Make it configurable via `os.environ.get("POLYMARKET_CLOB_HOST", "https://clob-v2.polymarket.com")`. After cutover, V2 takes over the production URL `https://clob.polymarket.com`, so I want to flip the env var rather than re-edit code.

Show me the diff. If multiple modules hardcode the host, centralize it through a single config module (or settings object).
Update my bot to point its CLOB base URL at the V2 staging endpoint so I can test before the April 28 cutover:

  https://clob-v2.polymarket.com

Make it configurable via env var `POLYMARKET_CLOB_HOST`, read the way that's idiomatic for this language (`process.env`, `os.Getenv`, `std::env::var`, `getenv`, etc.). Default to the staging URL. After cutover, V2 takes over the production URL `https://clob.polymarket.com`.

Show me the diff. Make sure REST POST /order calls AND any auth / market-info calls AND WebSocket connection setup all go through the same configurable host.
i After pasting: read your agent's response and review the diff. It may ask whether you want to use a different env var name or where to put the config helper. Approve the changes, then mark Done and continue.
Step 3 of 15
03
Upgrade the TypeScript SDK to @polymarket/clob-client-v2
TYPESCRIPT · npm / yarn / pnpm

The legacy @polymarket/clob-client stops working after cutover. Swap it out and update every import site.

Paste into your agent · typescript
Upgrade my project's Polymarket CLOB SDK from V1 to V2.

1. Remove these from package.json: `@polymarket/clob-client`, `@polymarket/builder-signing-sdk`.
2. Add `@polymarket/clob-client-v2` at version `^1.0.0`.
3. Run npm install / yarn install / pnpm install (whichever this project uses — detect from lockfiles).
4. Update every import site from `@polymarket/clob-client` → `@polymarket/clob-client-v2`. Watch out for re-exports and barrel files.
5. Do not delete the old packages from package.json until you've verified there are zero remaining imports.
6. Run the type-checker (`tsc --noEmit` or equivalent) and report any new errors. Don't fix them yet — we'll address them in Step 4 and Step 5.

Show me the diff before applying and the type-check output after.
i After pasting: the agent will run package manager commands. Watch for prompts asking to confirm install or to choose a package manager if multiple lockfiles are detected. Type errors after install are expected — later steps fix them.
Step 4 of 15
03
Upgrade the Python SDK to py-clob-client-v2
PYTHON · pip / poetry / pipenv

The legacy py-clob-client stops working after cutover. Swap to py-clob-client-v2 and rewrite imports.

Paste into your agent · python
Upgrade my project's Polymarket CLOB SDK from V1 to V2.

1. Remove `py-clob-client` from requirements.txt / pyproject.toml / Pipfile / setup.cfg / setup.py (whichever this project uses — detect first).
2. Add `py-clob-client-v2==1.0.0` (or the latest 1.x).
3. Update every import site from `from py_clob_client...` → `from py_clob_client_v2...`. Note the underscore vs hyphen difference between PyPI name and import name.
4. If the project uses a virtualenv, poetry env, or pipenv shell, reinstall.
5. Run `python -m py_compile` on every changed file (or `mypy`/`pyright` if configured) and report errors. Don't fix them yet — we'll address them in Step 4 and Step 5.

Show me the diff before applying and the post-install output.
i After pasting: the agent will run pip / poetry / pipenv commands. Watch for activation prompts if you use a virtualenv or for choices about which dependency tool to use. Type-check errors after install are expected and get fixed in later steps.
Step 4 of 15
04
Refactor every ClobClient constructor to V2's options-object form
TS + PYTHON SDK · Breaking change

V2 ships a single options-object constructor. chainId is renamed to chain. tickSizeTtlMs is gone. The builderConfig shape changed from HMAC creds to a single { builderCode } field — we'll wire the builder code in Step 6.

Paste into your agent · sdk
Refactor every `ClobClient` instantiation in this codebase to V2 style:

- V2 takes a single options object instead of positional args.
- The `chainId` parameter is renamed to `chain`. Same value (137 for Polygon mainnet, 80002 for Amoy testnet).
- Remove the `tickSizeTtlMs` argument entirely — it's no longer configurable in V2.
- Remove `geoBlockToken` from constructor config if present.
- The `builderConfig` shape changed: in V1 it wrapped `BuilderApiKeyCreds` from `@polymarket/builder-signing-sdk`. In V2 it's just `{ builderCode: string }`. We'll wire the actual builder code in a later step — for now leave `builderConfig` out, or stub it as an empty `{}` if the call site requires it.
- Other args (`signer`, `creds`, `signatureType`, `funderAddress`, `useServerTime`, `getSigner`, `retryOnError`, `throwOnError`) keep the same names and meanings — just move them into the options object.

Find every constructor call (TS or Python), apply the refactor, and show me the diffs. Also flag any callers that pass arguments positionally beyond what V2 accepts. Run the type-checker / `python -m py_compile` afterward and report.
Refactor every `new ClobClient(...)` call in this codebase to V2 style.

- V2 takes a single options object instead of positional args.
- Rename `chainId` → `chain`. Same value (137 mainnet, 80002 Amoy).
- Remove the `tickSizeTtlMs` argument entirely — no longer configurable in V2.
- Remove `geoBlockToken` from constructor config if present.
- The `builderConfig` shape changed: V1 wrapped `BuilderApiKeyCreds` from `@polymarket/builder-signing-sdk`. V2 is just `{ builderCode: string }`. Leave it out for now or stub it as `{}` — Step 6 wires it up.
- Other args (`signer`, `creds`, `signatureType`, `funderAddress`, `useServerTime`, `getSigner`, `retryOnError`, `throwOnError`) keep the same names — just move them into the options object.

Find every constructor call, apply the refactor, run `tsc --noEmit` (or the project's type-check script), and show me diffs plus any new type errors.
Refactor every `ClobClient(...)` instantiation in this codebase to V2 style.

- V2 takes a single options object (`ClobClientConfig` dataclass / dict) instead of positional args.
- Rename `chain_id` → `chain`. Same value (137 mainnet, 80002 Amoy).
- Remove `tick_size_ttl_ms` if passed — no longer configurable in V2.
- Remove `geo_block_token` from constructor config if present.
- The `builder_config` shape changed: V1 wrapped `BuilderApiKeyCreds`. V2 is just `{"builder_code": "0x..."}`. Leave it out for now — Step 6 wires it up.
- Other args (`signer`, `creds`, `signature_type`, `funder_address`, `use_server_time`, `get_signer`, `retry_on_error`, `throw_on_error`) keep the same names — just move into the options object.

Find every constructor call, apply the refactor, run `python -m py_compile` on the changed files (and `mypy`/`pyright` if configured), and show me diffs plus any new errors.
i After pasting: read the diff carefully — the constructor signature shift is the most error-prone change. The agent may ask which optional args are still in use. Type errors are expected and get resolved by Step 5.
Step 5 of 15
05
Migrate every order object to the V2 shape
TS + PYTHON SDK · Drop nonce / feeRateBps / taker

Three V1 fields are no longer user-settable. The protocol fills them in now (or replaces them with timestamp). One new optional field, builderCode, is wired up in Step 6.

Paste into your agent · sdk
Migrate every order construction in this codebase to the V2 Order shape.

Remove these fields if present (the SDK / protocol now sets them):
  - `feeRateBps` — V2 fees are operator-set at match time, not embedded in the order.
  - `nonce` — V2 uses `timestamp` (ms) for per-address uniqueness instead. The SDK populates it; you don't pass it.
  - `taker` — open orders no longer specify a counterparty.

Keep these fields as-is:
  - `tokenID`, `price`, `size`, `side`, `expiration`, `salt`, `signatureType`, `maker`, `signer`.

Add this optional field, but leave it commented or unset for now — Step 6 wires it up:
  - `builderCode` (TypeScript) / `builder_code` (Python).

For market orders (UserMarketOrder / UserMarketOrderV2 / Python equivalents), surface an optional `userUSDCBalance` parameter in any helper that builds them, so the SDK can compute fee-adjusted fill amounts on market BUYs.

Also: delete any manual fee-calculation helpers, hardcoded `feeRateBps` constants, or formulas like `fee = rate * p * (1 - p)`. The SDK now handles fee math.

Walk through each order-construction site and show diffs. Flag any tests that hardcode the old fields. After applying, run the type-checker / `python -m py_compile` and report.
Migrate every order construction in this codebase to the V2 Order shape.

Remove these fields from every `UserOrder` / `UserMarketOrder` literal (the SDK / protocol now sets them):
  - `feeRateBps` — V2 fees are operator-set at match time, not embedded in the order.
  - `nonce` — V2 uses `timestamp` (ms) for per-address uniqueness. The SDK populates it.
  - `taker` — open orders no longer specify a counterparty.

Keep these as-is: `tokenID`, `price`, `size`, `side`, `expiration`, `salt`, `signatureType`, `maker`, `signer`.

Add this optional field, but leave it unset for now — Step 6 wires it:
  - `builderCode`

For market orders (`UserMarketOrder` → `UserMarketOrderV2`), surface an optional `userUSDCBalance` parameter in any helper that builds market BUYs, so the SDK can compute fee-adjusted fill amounts.

Delete any manual fee-calculation helpers, hardcoded `feeRateBps` constants, or formulas like `fee = rate * p * (1 - p)`. The SDK handles fee math now.

Walk through each order-construction site, show diffs, flag any tests that hardcode the old fields, and run the type-checker.
Migrate every order construction in this codebase to the V2 Order shape.

Remove these fields from every `OrderArgs` / `MarketOrderArgs` (or equivalent) literal (the SDK / protocol now sets them):
  - `fee_rate_bps` — V2 fees are operator-set at match time, not embedded in the order.
  - `nonce` — V2 uses `timestamp` (ms) for per-address uniqueness. The SDK populates it.
  - `taker` — open orders no longer specify a counterparty.

Keep these as-is: `token_id`, `price`, `size`, `side`, `expiration`, `salt`, `signature_type`, `maker`, `signer`.

Add this optional field, but leave it unset for now — Step 6 wires it:
  - `builder_code`

For market orders (`MarketOrderArgs`), surface an optional `user_usdc_balance` parameter in any helper that builds market BUYs, so the SDK can compute fee-adjusted fill amounts.

Delete any manual fee-calculation helpers, hardcoded `FEE_RATE_BPS` constants, or formulas like `fee = rate * p * (1 - p)`. The SDK handles fee math now.

Walk through each order-construction site, show diffs, flag any tests that hardcode the old fields, and run `python -m py_compile`.
i After pasting: the agent walks every order site. It may ask whether to delete a manual fee helper or just deprecate it — tell it to delete. Once diffs are applied and the type-check passes, mark Done.
Step 6 of 15
06
Replace the HMAC builder flow with the V2 builderCode field
TS + PYTHON SDK · Builders only

Skip this step if you're not a builder. If you have a builder code, this prompt removes the V1 HMAC plumbing and switches to the bytes32 builderCode field on each order. The HMAC builder API key is not fully retired — it's still used by the gasless Relayer if your bot uses it.

Outside your agent · get your builder code first

Open polymarket.com/settings?tab=builder in your browser. (You can also click your avatar in the top right of polymarket.com, open the dropdown, and select Settings → Builders.)

If you don't already have a builder profile, set one up — takes about 30 seconds. Copy the bytes32 hex string labelled "Builder Code".

Add it to your bot's environment as POLY_BUILDER_CODE (export it in your shell, add to .env, or paste it into your secret manager — whatever your bot reads). The agent prompt below assumes the env var is set.

Paste into your agent · sdk
Migrate my builder integration from the V1 HMAC-header flow to the V2 builderCode flow.

V1 plumbing to delete (if my bot uses it):
  - Imports of `@polymarket/builder-signing-sdk` (TS) or its Python equivalent.
  - Reads of `POLY_BUILDER_API_KEY`, `POLY_BUILDER_SECRET`, `POLY_BUILDER_PASSPHRASE`.
  - The `BuilderApiKeyCreds` / `BuilderConfig` wrapping of those.
  - Sending of `POLY_BUILDER_API_KEY`, `POLY_BUILDER_SECRET`, `POLY_BUILDER_PASSPHRASE`, `POLY_BUILDER_SIGNATURE` request headers.

V2 replacement:
  - One env var: `POLY_BUILDER_CODE` — a bytes32 hex string from https://polymarket.com/settings?tab=builder.
  - Set it on the client either:
      a) Per order, via the `builderCode` field on each UserOrderV2 / UserMarketOrderV2 (or Python equivalent), OR
      b) Once at construction: `builderConfig: { builderCode: process.env.POLY_BUILDER_CODE }` so every order auto-includes it.

Pick (b) by default if my bot has a single attribution identity. Use (a) only if my bot signs orders for multiple builders.

Important: the HMAC builder API key is NOT fully retired — Polymarket's gasless Relayer still uses it. If the bot integrates with the Relayer (look for relayer URLs or relayer-specific request flows), keep the relayer credentials but separate them from the order-signing flow.

Show diffs and flag if you find any remaining `POLY_BUILDER_*` references after the migration. Confirm zero matches for `builder-signing-sdk` after applying.
Migrate my builder integration from the V1 HMAC-header flow to the V2 builderCode flow.

Delete:
  - Imports of `@polymarket/builder-signing-sdk`.
  - Reads of `POLY_BUILDER_API_KEY`, `POLY_BUILDER_SECRET`, `POLY_BUILDER_PASSPHRASE`.
  - Any `BuilderApiKeyCreds` / `BuilderConfig` wrapping of those.
  - Sending of `POLY_BUILDER_API_KEY`, `POLY_BUILDER_SECRET`, `POLY_BUILDER_PASSPHRASE`, `POLY_BUILDER_SIGNATURE` request headers.

Add:
  - Env var `POLY_BUILDER_CODE` (a bytes32 hex string I've already set; it came from https://polymarket.com/settings?tab=builder).
  - Set it on the client either:
      a) Per order, via `builderCode` on each UserOrderV2 / UserMarketOrderV2, OR
      b) Once at construction: `builderConfig: { builderCode: process.env.POLY_BUILDER_CODE }` so every order auto-includes it.

Default to (b) if the bot has a single attribution identity. Use (a) only if it signs for multiple builders.

Important: the HMAC builder API key is NOT fully retired — Polymarket's gasless Relayer still uses it. If the bot integrates with the Relayer, keep those credentials but separate them from the order-signing flow.

Show diffs and confirm zero remaining `builder-signing-sdk` or `POLY_BUILDER_API_KEY` references after the migration.
Migrate my builder integration from the V1 HMAC-header flow to the V2 builder_code flow.

Delete:
  - Any `py_builder_signing_sdk` / `BuilderApiKeyCreds` / `BuilderConfig` imports from the V1 flow.
  - Reads of `POLY_BUILDER_API_KEY`, `POLY_BUILDER_SECRET`, `POLY_BUILDER_PASSPHRASE`.
  - Sending of `POLY_BUILDER_API_KEY`, `POLY_BUILDER_SECRET`, `POLY_BUILDER_PASSPHRASE`, `POLY_BUILDER_SIGNATURE` request headers.

Add:
  - Env var `POLY_BUILDER_CODE` (a bytes32 hex string I've already set; it came from https://polymarket.com/settings?tab=builder).
  - Set it on the client either:
      a) Per order, via `builder_code` on each `OrderArgs` / `MarketOrderArgs`, OR
      b) Once at construction: `builder_config={"builder_code": os.environ["POLY_BUILDER_CODE"]}` so every order auto-includes it.

Default to (b) if the bot has a single attribution identity.

Important: the HMAC builder API key is NOT fully retired — Polymarket's gasless Relayer still uses it. If the bot integrates with the Relayer, keep those credentials but separate them from the order-signing flow.

Show diffs and confirm zero remaining `POLY_BUILDER_API_KEY` / `POLY_BUILDER_SECRET` / `POLY_BUILDER_PASSPHRASE` references after the migration.
i After pasting: the agent will ask whether you want per-order or constructor-level attribution — pick the one that matches your bot's design (single identity = constructor; multi-builder = per-order). Verify there are no remaining HMAC env-var reads when it's done.
Step 7 of 15
07
Drop manual fee math, use getClobMarketInfo
TS + PYTHON SDK · Operator-set fees

V2 fees are protocol-set per market: fee = C × feeRate × p × (1 - p). Makers are never charged. Surface the parameters via one SDK call instead of recomputing.

Paste into your agent · sdk
Update fee handling for V2.

1. Find any code that calculates fees manually — look for variables like `feeBps`, `FEE_RATE`, hardcoded multipliers (e.g. `0.02`), or formulas resembling `fee = rate * p * (1 - p)`. Delete or replace with calls to the SDK.

2. Where the bot needs to know per-market fee parameters, replace ad-hoc lookups with a single SDK call:
     TypeScript: `await client.getClobMarketInfo(conditionID)`
     Python:     `client.get_clob_market_info(condition_id)`

   The response shape is:
     mts  → minimum tick size
     mos  → minimum order size
     fd   → { r: feeRate, e: exponent, to: takerOnly }
     t    → [{ t: tokenID, o: outcome }, ...]
     rfqe → RFQ enabled flag

3. For market BUY orders, pass `userUSDCBalance` (the bot's available pUSD balance) to the order-construction helper so the SDK can compute fee-adjusted fill amounts. For market SELL orders this isn't needed.

4. Remember: makers are never charged fees in V2 — only takers pay. Strip any maker-fee assumptions.

Show diffs, surface any fee-related code that's now dead, and run the test suite if one exists.
Update fee handling for V2.

1. Find any code that calculates fees manually — `feeBps`, `FEE_RATE`, hardcoded multipliers like `0.02`, formulas like `rate * p * (1 - p)`. Delete them.

2. Replace ad-hoc fee lookups with: `const info = await client.getClobMarketInfo(conditionID);`
   Response shape:
     mts  → minimum tick size
     mos  → minimum order size
     fd   → { r: feeRate, e: exponent, to: takerOnly }
     t    → [{ t: tokenID, o: outcome }, ...]
     rfqe → RFQ enabled flag

3. For market BUY orders, pass `userUSDCBalance` (the bot's pUSD balance) to the order-construction helper so the SDK computes fee-adjusted fill amounts. Not needed for SELLs.

4. Makers are NEVER charged fees in V2 — strip any maker-fee assumptions.

Show diffs, flag dead code, and run the test suite.
Update fee handling for V2.

1. Find any manual fee calculations — `fee_bps`, `FEE_RATE`, hardcoded multipliers like `0.02`, formulas like `rate * p * (1 - p)`. Delete them.

2. Replace ad-hoc fee lookups with: `info = client.get_clob_market_info(condition_id)`
   Response shape:
     info["mts"]  → minimum tick size
     info["mos"]  → minimum order size
     info["fd"]   → { "r": fee_rate, "e": exponent, "to": taker_only }
     info["t"]    → [{ "t": token_id, "o": outcome }, ...]
     info["rfqe"] → RFQ enabled flag

3. For market BUY orders, pass `user_usdc_balance` (the bot's pUSD balance) to the order-construction helper so the SDK computes fee-adjusted fill amounts. Not needed for SELLs.

4. Makers are NEVER charged fees in V2 — strip any maker-fee assumptions.

Show diffs, flag dead code, and run pytest if there are tests.
i After pasting: the agent may surface ambiguous fee logic and ask whether to delete or refactor. Default to delete — the SDK does the math now. Confirm tests still pass.
Step 8 of 15
08
Update raw EIP-712 signing — domain, Order type, contract addresses
RAW HTTP / GO / RUST / NO-SDK · Wire-protocol change

For Go, Rust, or any language without an official Polymarket SDK, you sign orders by hand. V2 bumps the EIP-712 domain version from "1" to "2", swaps the verifying contracts, and rewrites the Order struct.

Outside your agent · if you're a builder

If you want builder attribution, grab your builder code from polymarket.com/settings?tab=builder first (avatar dropdown → Settings → Builders) and export it as POLY_BUILDER_CODE. Without attribution, the new bytes32 builder field is just zero bytes.

Paste into your agent · raw signing
This project signs Polymarket orders directly via EIP-712 (no SDK). Migrate the signing code to V2:

1. Bump the EIP-712 Exchange domain version `"1"` → `"2"`.

2. Update the `verifyingContract`:
     CTF Exchange:      0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E (V1) → 0xE111180000d2663C0091e4f400237545B87B996B (V2)
     Neg Risk Exchange: 0xC5d563A36AE78145C45a50134d48A1215220f80a (V1) → 0xe2222d279d744050d28e00520010520000310F59 (V2)
   `chainId` stays 137. The `name` stays `"Polymarket CTF Exchange"`.

3. Do NOT change the `ClobAuthDomain` used for L1 API auth — it stays at version `"1"`. L1/L2 auth is identical between V1 and V2.

4. Update the Order EIP-712 type. Drop these fields:
     address taker
     uint256 expiration   (stays in the request body but not in the signed struct)
     uint256 nonce
     uint256 feeRateBps
   Add these fields:
     uint256 timestamp     // ms since epoch — replaces nonce for uniqueness, NOT an expiration
     bytes32 metadata
     bytes32 builder       // 32 zero bytes unless attaching a builder code from POLY_BUILDER_CODE

5. Update the order value to sign accordingly. Note that `side` is encoded as `uint8` in the signing payload (0 = BUY, 1 = SELL), even though the wire body uses the strings "BUY" / "SELL". No change from V1 there.

6. Update the POST /order body to match: drop `taker`, `nonce`, `feeRateBps`. Add `timestamp` (string ms), `metadata` (bytes32 hex), `builder` (bytes32 hex). `expiration` stays in the request body even though it's no longer in the signed struct.

7. Remove all `POLY_BUILDER_API_KEY` / `POLY_BUILDER_SECRET` / `POLY_BUILDER_PASSPHRASE` / `POLY_BUILDER_SIGNATURE` request headers. The other API auth headers (`POLY_ADDRESS`, `POLY_SIGNATURE`, `POLY_TIMESTAMP`, `POLY_API_KEY`, `POLY_PASSPHRASE`) are unchanged.

8. If the project has unit tests for the encoder/signer, update fixtures and run them. If not, write a quick smoke test that hashes a known V2 order and prints the EIP-712 digest so I can compare against a reference.

Show diffs for every signing/order-encoding file and the test output.
i After pasting: the agent may ask which language-specific signing library you use (eth_account, ethers-rs, go-ethereum) before changing fixtures. Approve any test rewrites — the V2 Order struct hash will not match V1 fixtures.
Step 9 of 15
09
Migrate collateral — USDC.e → pUSD
UNIVERSAL · API-only bots: wrap()

pUSD (Polymarket USD) replaces USDC.e. It's a standard ERC-20 on Polygon, backed onchain by USDC. polymarket.com handles wrapping for UI users; API-only bots call wrap() on the Collateral Onramp contract themselves.

Outside your agent · choose your wrap path

If you also trade through polymarket.com: open polymarket.com, connect the same wallet your bot uses, and approve the one-time pUSD migration prompt the UI will surface. Your USDC.e becomes pUSD — no code needed for that part.

If your bot is API-only (no UI usage): the agent prompt below stages programmatic wrap() code via the Collateral Onramp contract. Stay in your agent.

Either way, after the wrap your bot's pUSD balance becomes the live trading collateral. USDC.e in the same wallet is no longer used by the V2 Exchange.

Paste into your agent · universal
Update my bot for the USDC.e → pUSD collateral migration.

1. Find every hardcoded reference to the USDC.e address or the symbol "USDC.e" in code, configs, and tests. List them.

2. If my bot is API-only (no polymarket.com UI), I need to wrap USDC.e into pUSD before trading. Add a helper that calls `wrap(amount)` on the Collateral Onramp contract. See https://docs.polymarket.com/concepts/pusd for the contract address and ABI snippet, and https://docs.polymarket.com/resources/contracts for canonical V2 addresses. Make the wrap a one-shot startup task gated by a `--wrap` CLI flag or env var (e.g. `WRAP_ON_START=1`) so it doesn't run on every restart.

3. Update balance-checking code to read pUSD balance instead of (or in addition to) USDC.e. If the bot logs balances, log both during the transition.

4. If my bot has approval-management code (`approve(spender, amount)`), make sure pUSD is approved for both V2 Exchange contract addresses:
     CTF Exchange:      0xE111180000d2663C0091e4f400237545B87B996B
     Neg Risk Exchange: 0xe2222d279d744050d28e00520010520000310F59

5. Don't run the wrap or approval transactions yet — just stage the code and show me the diffs. I'll execute them as part of Step 11's E2E test.

Show diffs. Flag anywhere balance / approval logic still assumes USDC.e.
Update my bot for the USDC.e → pUSD collateral migration.

1. Find every hardcoded reference to the USDC.e address or the symbol "USDC.e" in code, configs, and tests. List them.

2. If the bot is API-only (no polymarket.com UI), wrap USDC.e into pUSD via the Collateral Onramp's `wrap()` function. See https://docs.polymarket.com/concepts/pusd for the address + ABI and https://docs.polymarket.com/resources/contracts for canonical V2 addresses. Add a helper using ethers.js (or whatever this bot already uses) and gate it behind an env flag like `WRAP_ON_START=1` so it only runs when explicitly requested.

3. Update balance-checking code to read pUSD balance instead of (or in addition to) USDC.e.

4. If the bot has approval-management code (`approve(spender, amount)`), make sure pUSD is approved for both V2 Exchange contracts:
     CTF Exchange:      0xE111180000d2663C0091e4f400237545B87B996B
     Neg Risk Exchange: 0xe2222d279d744050d28e00520010520000310F59

5. Stage the code only — don't execute the wrap or approval transactions yet. Step 11 will run them as part of E2E.

Show diffs. Flag anywhere balance / approval logic still assumes USDC.e.
Update my bot for the USDC.e → pUSD collateral migration.

1. Find every hardcoded reference to the USDC.e address or the symbol "USDC.e" in code, configs, and tests. List them.

2. If the bot is API-only (no polymarket.com UI), wrap USDC.e into pUSD via the Collateral Onramp's `wrap(amount)` function. See https://docs.polymarket.com/concepts/pusd for address + ABI. Use whatever EVM client the bot already imports (web3.py / async_eth / brownie). Gate it behind an env flag like `WRAP_ON_START=1`.

3. Update balance-checking code to read pUSD balance instead of (or in addition to) USDC.e.

4. If the bot has approval-management code (`approve(spender, amount)`), make sure pUSD is approved for both V2 Exchange contracts:
     CTF Exchange:      0xE111180000d2663C0091e4f400237545B87B996B
     Neg Risk Exchange: 0xe2222d279d744050d28e00520010520000310F59

5. Stage the code only — don't execute transactions yet. Step 11 will run them as part of E2E.

Show diffs. Flag anywhere balance / approval logic still assumes USDC.e.
Update my bot for the USDC.e → pUSD collateral migration.

1. Find every hardcoded reference to the USDC.e address or the symbol "USDC.e" in code, configs, and tests.

2. If the bot is API-only, wrap USDC.e into pUSD via the Collateral Onramp's `wrap(amount)` function. See https://docs.polymarket.com/concepts/pusd for address + ABI. Use whatever EVM client this language has (go-ethereum, ethers-rs, web3j, etc.). Gate it behind an env flag like `WRAP_ON_START=1`.

3. Update balance-checking code to read pUSD instead of (or in addition to) USDC.e.

4. If the bot has approval-management code, ensure pUSD is approved for both V2 Exchange contracts:
     CTF Exchange:      0xE111180000d2663C0091e4f400237545B87B996B
     Neg Risk Exchange: 0xe2222d279d744050d28e00520010520000310F59

5. Stage the code only — don't execute transactions yet.

Show diffs.
i After pasting: the agent may ask whether to add a CLI flag, env var, or admin endpoint to trigger the wrap. Pick what fits your deploy story. Don't run the actual on-chain wrap yet — Step 11 does that during the E2E test on staging.
Step 10 of 15
10
Sanity-check WebSocket / streaming code
UNIVERSAL · Streamers only

WebSocket URLs and most payload shapes are unchanged. The risk is hidden assumptions in your handlers about V1 fields like nonce or feeRateBps that may now be missing or zero on V2 events.

Paste into your agent · universal
Sanity-check my WebSocket / streaming integration against V2:

- WebSocket URLs are unchanged — confirm I'm still connecting to the same hosts.
- Most message payloads are unchanged.
- The `fee_rate_bps` field on `last_trade_price` events continues to reflect the actual fee charged on the trade — DON'T remove handling for it.
- Verify that any code reacting to order-state events doesn't assume the V1 Order shape (e.g. doesn't expect `nonce` or `feeRateBps` to come back on order updates from the bot's own orders).
- Verify any deserializer / Pydantic model / Go struct / Rust serde struct for incoming messages tolerates missing or new fields. New V2 fields like `timestamp`, `metadata`, `builder` may appear — the bot should not reject messages it doesn't fully understand.

List anywhere you see strict assumptions about old field names. Show diffs only where actually needed.
i After pasting: mostly read-only. The agent should report "no changes needed" or surface 1–2 strict deserializers to relax. Confirm anything it suggests, then mark Done.
Step 11 of 15
11
End-to-end test against the V2 staging endpoint
UNIVERSAL · Real network calls

All test market condition IDs are listed in the Test markets reference section below. Use a fee-enabled market to verify fee math; resolve any condition ID into token IDs via the Gamma API.

Outside your agent · verify attribution

After the test order fills, open builders.polymarket.com in your browser and check that your trade shows up under your builder code (if you set POLY_BUILDER_CODE in Step 6/8). Attribution can take a minute to surface.

Paste into your agent · universal
Run an end-to-end test against `https://clob-v2.polymarket.com` using a test market with liquidity. Use one of these fee-enabled condition IDs (verifies fee handling end-to-end):

  0xaf5e903876ad42de97e1cf02c2ef8484df69bcfc5541b96a400116557d1e504e
  0xe9955a31d76ea97457410f61c9b9f2d27ac6fbd8302ed1849d93133884f4fb3b
  0x8e6a263fc5f4dd9433b4ccbc9bb9e0d89f4de8a91d3a82d6b437fe73fd847ea5
  0xc7d462e462d8d369aee17c9e3ea8f113166cf9a342a6434a0b2a0f7588dc1bbf
  0x0440d08e4f534afa8ad9f616a239a19ebf7f2476bd802b1d54180435fa83463f
  0xd048b3e9ccad82c57a4eec953e75794aeb5e6d0e08bdb79b9e01b24e0049f16d
  0x88310713845c54e36f29791a1c2dc172b9819b645ba82d5f87560805cd2bb788
  0xffcab88076a28795281bb07a082c7003a4ff5671420086cb16ec09dfbf9aea68
  0x64a5bcfb0c76a75081c8be49c895a976d506fce8d21aa9d73f1099e502f2fa4e
  0x8babe4f4d0eff732d660fa02929a581cb8478ed2a1696c158290b2794d3d7ac0

Resolve a chosen condition ID into token IDs and metadata via:
  https://gamma-api.polymarket.com/markets?condition_ids=<id>

Walk through this test flow with me, executing each step and reporting before/after:
  1. (If API-only) wrap a small amount of USDC.e into pUSD via the Collateral Onramp.
  2. Fetch market info via getClobMarketInfo / get_clob_market_info. Print mts, mos, fd, t.
  3. Place a small limit order (well outside the spread so it doesn't fill) and confirm the API accepts the V2-shaped payload. Inspect the returned order object.
  4. Cancel that order via the SDK's cancel method.
  5. Place a tiny GTC order at a fillable price and let it sit briefly.
  6. Verify builder attribution (if `POLY_BUILDER_CODE` is set) appears on https://builders.polymarket.com.
  7. Stream the orderbook via WebSocket for 30s and confirm message handling works without exceptions.
  8. Place a market order with `userUSDCBalance` set and verify the fee-adjusted fill amount matches expectations within rounding tolerance.

Capture and report any errors, unexpected response shapes, or warnings.
Run an end-to-end test against `https://clob-v2.polymarket.com`. Use one of these fee-enabled condition IDs:

  0xaf5e903876ad42de97e1cf02c2ef8484df69bcfc5541b96a400116557d1e504e
  0xe9955a31d76ea97457410f61c9b9f2d27ac6fbd8302ed1849d93133884f4fb3b
  0x8e6a263fc5f4dd9433b4ccbc9bb9e0d89f4de8a91d3a82d6b437fe73fd847ea5
  0xc7d462e462d8d369aee17c9e3ea8f113166cf9a342a6434a0b2a0f7588dc1bbf
  0x0440d08e4f534afa8ad9f616a239a19ebf7f2476bd802b1d54180435fa83463f
  0xd048b3e9ccad82c57a4eec953e75794aeb5e6d0e08bdb79b9e01b24e0049f16d
  0x88310713845c54e36f29791a1c2dc172b9819b645ba82d5f87560805cd2bb788
  0xffcab88076a28795281bb07a082c7003a4ff5671420086cb16ec09dfbf9aea68
  0x64a5bcfb0c76a75081c8be49c895a976d506fce8d21aa9d73f1099e502f2fa4e
  0x8babe4f4d0eff732d660fa02929a581cb8478ed2a1696c158290b2794d3d7ac0

Resolve a chosen condition ID into token IDs via:
  https://gamma-api.polymarket.com/markets?condition_ids=<id>

Walk through this test flow, executing each step:
  1. (If API-only) wrap a small amount of USDC.e into pUSD.
  2. `await client.getClobMarketInfo(conditionID)` — print mts, mos, fd, t.
  3. Place a small limit order well outside the spread. Confirm the API accepts the V2-shaped payload. Inspect the returned order.
  4. Cancel that order via the SDK.
  5. Place a tiny GTC at a fillable price and let it sit briefly.
  6. If `POLY_BUILDER_CODE` is set, verify attribution at https://builders.polymarket.com.
  7. Stream the orderbook via WebSocket for 30s. Confirm no exceptions.
  8. Place a market order with `userUSDCBalance` set. Verify the fee-adjusted fill amount within rounding tolerance.

Capture and report errors and unexpected response shapes.
Run an end-to-end test against `https://clob-v2.polymarket.com`. Use one of these fee-enabled condition IDs:

  0xaf5e903876ad42de97e1cf02c2ef8484df69bcfc5541b96a400116557d1e504e
  0xe9955a31d76ea97457410f61c9b9f2d27ac6fbd8302ed1849d93133884f4fb3b
  0x8e6a263fc5f4dd9433b4ccbc9bb9e0d89f4de8a91d3a82d6b437fe73fd847ea5
  0xc7d462e462d8d369aee17c9e3ea8f113166cf9a342a6434a0b2a0f7588dc1bbf
  0x0440d08e4f534afa8ad9f616a239a19ebf7f2476bd802b1d54180435fa83463f
  0xd048b3e9ccad82c57a4eec953e75794aeb5e6d0e08bdb79b9e01b24e0049f16d
  0x88310713845c54e36f29791a1c2dc172b9819b645ba82d5f87560805cd2bb788
  0xffcab88076a28795281bb07a082c7003a4ff5671420086cb16ec09dfbf9aea68
  0x64a5bcfb0c76a75081c8be49c895a976d506fce8d21aa9d73f1099e502f2fa4e
  0x8babe4f4d0eff732d660fa02929a581cb8478ed2a1696c158290b2794d3d7ac0

Resolve a chosen condition ID into token IDs via:
  https://gamma-api.polymarket.com/markets?condition_ids=<id>

Walk through this test flow, executing each step:
  1. (If API-only) wrap a small amount of USDC.e into pUSD.
  2. `info = client.get_clob_market_info(condition_id)` — print mts, mos, fd, t.
  3. Place a small limit order well outside the spread. Confirm the API accepts the V2-shaped payload. Inspect the returned order.
  4. Cancel that order via the SDK.
  5. Place a tiny GTC at a fillable price and let it sit briefly.
  6. If `POLY_BUILDER_CODE` is set, verify attribution at https://builders.polymarket.com.
  7. Stream the orderbook via WebSocket for 30s. Confirm no exceptions.
  8. Place a market order with `user_usdc_balance` set. Verify the fee-adjusted fill amount within rounding tolerance.

Capture and report errors and unexpected response shapes.
Run an end-to-end test against `https://clob-v2.polymarket.com` using your raw HTTP signing path. Use one of these fee-enabled condition IDs:

  0xaf5e903876ad42de97e1cf02c2ef8484df69bcfc5541b96a400116557d1e504e
  0xe9955a31d76ea97457410f61c9b9f2d27ac6fbd8302ed1849d93133884f4fb3b
  0x8e6a263fc5f4dd9433b4ccbc9bb9e0d89f4de8a91d3a82d6b437fe73fd847ea5
  0xc7d462e462d8d369aee17c9e3ea8f113166cf9a342a6434a0b2a0f7588dc1bbf
  0x0440d08e4f534afa8ad9f616a239a19ebf7f2476bd802b1d54180435fa83463f
  0xd048b3e9ccad82c57a4eec953e75794aeb5e6d0e08bdb79b9e01b24e0049f16d
  0x88310713845c54e36f29791a1c2dc172b9819b645ba82d5f87560805cd2bb788
  0xffcab88076a28795281bb07a082c7003a4ff5671420086cb16ec09dfbf9aea68
  0x64a5bcfb0c76a75081c8be49c895a976d506fce8d21aa9d73f1099e502f2fa4e
  0x8babe4f4d0eff732d660fa02929a581cb8478ed2a1696c158290b2794d3d7ac0

Resolve a chosen condition ID into token IDs via:
  https://gamma-api.polymarket.com/markets?condition_ids=<id>

Walk through this test flow, executing each step:
  1. (If API-only) wrap a small amount of USDC.e into pUSD via the Collateral Onramp.
  2. GET market info from the CLOB API. Print the response.
  3. Build a V2 Order struct (with `timestamp`, `metadata`, `builder` populated; no `nonce`, `feeRateBps`, or `taker`). Sign with the V2 EIP-712 domain (version "2", verifyingContract per market type). POST to /order.
  4. Inspect the response. Verify the API accepted the V2-shaped payload.
  5. Cancel the order via the cancel endpoint.
  6. If `POLY_BUILDER_CODE` is set, verify attribution at https://builders.polymarket.com.
  7. Connect to the orderbook WebSocket for 30s. Confirm no parse errors.

Print every signed digest and request body so I can compare against a reference. Capture and report errors.
i After pasting: this is the longest step. The agent will ask which condition ID to use, may pause for wallet confirmations, and will likely surface unexpected response fields. Treat warnings as findings, not failures, unless the test order outright fails.
Step 12 of 15
12
Write the cutover-day runbook
UNIVERSAL · April 28 prep

Capture the exact steps you'll execute on the day. The order book wipe means you need a re-placement plan ready before the window starts.

Outside your agent · subscribe to status

The exact maintenance-window minute is announced day-of. Subscribe to status.polymarket.com and join the Polymarket Discord and/or Telegram for the precise start time.

Paste into your agent · universal
Prep the bot for the April 28, 2026 ~11:00 UTC cutover.

1. Verify my V2 SDK version is the latest (`@polymarket/clob-client-v2@^1.0.0` or `py-clob-client-v2==1.0.0` minimum). If a newer patch exists, bump it.

2. Confirm my CLOB host is configurable and that I can flip it from `clob-v2.polymarket.com` (test) → `clob.polymarket.com` (production) by changing one env var. After cutover, V2 takes over the production URL automatically.

3. Create or update `RUNBOOK.md` with:
     - Exact start time / target window for ~1 hour of expected downtime (April 28, 2026 ~11:00 UTC).
     - Steps to pause the bot before the window (graceful drain).
     - Steps to flip env vars (host) and restart the bot after the window.
     - The expectation that ALL open orders will be wiped — list any open positions / standing orders / market-making strategies that need to be re-placed after cutover, with exact recreation commands.
     - A sanity-check script (e.g. `scripts/v2-smoke.ts` or `scripts/v2_smoke.py`) to run once V2 is live: calls `getClobMarketInfo` against a known market, performs a no-op auth check, and prints the SDK version.
     - Rollback notes (there is no V1 fallback after cutover — if V2 misbehaves, the only option is to halt the bot and wait for Polymarket fixes).

Show me the runbook diff and the smoke-test script. Don't execute the script yet.
Prep the bot for the April 28, 2026 ~11:00 UTC cutover.

1. Verify `@polymarket/clob-client-v2` is at the latest 1.x in package.json. Bump if a newer patch exists.

2. Confirm my CLOB host is configurable and that I can flip it from `clob-v2.polymarket.com` (test) → `clob.polymarket.com` (production) by changing one env var.

3. Create or update `RUNBOOK.md`:
     - Cutover window: April 28, 2026 ~11:00 UTC, ~1 hour.
     - Steps to drain and pause the bot before the window.
     - Steps to flip env vars and restart after.
     - List of resting orders / market-making positions that need recreation, with exact commands.
     - A `scripts/v2-smoke.ts` smoke-test script that calls `getClobMarketInfo` against a known market, performs a no-op auth check, and prints the SDK version.
     - Rollback notes: there is no V1 fallback after cutover. If V2 misbehaves, halt and wait.

Show me the runbook diff and the smoke-test script. Don't execute it yet.
Prep the bot for the April 28, 2026 ~11:00 UTC cutover.

1. Verify `py-clob-client-v2` is at the latest 1.x in requirements.txt / pyproject.toml. Bump if a newer patch exists.

2. Confirm my CLOB host is configurable and that I can flip it from `clob-v2.polymarket.com` (test) → `clob.polymarket.com` (production) by changing one env var.

3. Create or update `RUNBOOK.md`:
     - Cutover window: April 28, 2026 ~11:00 UTC, ~1 hour.
     - Steps to drain and pause the bot before the window.
     - Steps to flip env vars and restart after.
     - List of resting orders / market-making positions that need recreation, with exact commands.
     - A `scripts/v2_smoke.py` smoke-test script that calls `client.get_clob_market_info(...)` against a known market, performs a no-op auth check, and prints the package version.
     - Rollback notes: there is no V1 fallback after cutover.

Show me the runbook diff and the smoke-test script. Don't execute it yet.
Prep the bot for the April 28, 2026 ~11:00 UTC cutover.

1. Confirm my signing code uses the V2 EIP-712 domain (version "2", new verifyingContract addresses) — re-check the audit from Step 8.

2. Confirm my CLOB host is configurable and that I can flip from `clob-v2.polymarket.com` → `clob.polymarket.com` by changing one env var.

3. Create or update `RUNBOOK.md`:
     - Cutover window: April 28, 2026 ~11:00 UTC, ~1 hour.
     - Drain / pause / restart steps.
     - List of resting orders that need recreation.
     - A smoke-test script in this language: hits GET /markets with a known condition ID, signs a no-op auth probe, and prints versions.
     - Rollback: no V1 fallback exists.

Show me the runbook diff and the smoke-test script.
i After pasting: review the runbook carefully — this is the document you'll execute on cutover day. Add notes the agent doesn't know (paging schedules, on-call rotations, pause-deploy commands specific to your infra).
Step 13 of 15
13
Post-cutover smoke test & verification
UNIVERSAL · After April 28

Run this only after the cutover window has officially closed and Polymarket announces V2 is live.

Outside your agent

Confirm the window is closed via status.polymarket.com before running this prompt. After the smoke test, double-check builder attribution at builders.polymarket.com if you're a builder.

Paste into your agent · universal
V2 is live. Run a post-cutover smoke test.

1. Confirm `https://clob.polymarket.com` now serves V2 by checking server headers / version endpoint, or by hitting an endpoint that's V2-only (e.g. `getClobMarketInfo`).

2. Re-place any open orders that the cutover wiped, using the V2 codepath. Reference the recreation commands documented in RUNBOOK.md from Step 12.

3. Verify at least one fill arrives correctly with the expected fee-adjusted economics. Compare the filled amount against `getClobMarketInfo`-derived fee math.

4. Check that builder attribution still appears on https://builders.polymarket.com if I'm a builder.

5. Tail logs for any deserialization warnings or unexpected fields — V2 may include new fields the bot doesn't yet handle. Treat these as non-fatal but log them.

6. Update `RUNBOOK.md` to mark the migration complete, remove staging-specific notes, and document anything that surprised us during cutover for the next migration.

Report any anomalies as a punch list and propose fixes for the most urgent.
i After pasting: the agent will execute live trades. Watch for unexpected behavior in the first few fills — fee handling, attribution, latency. If anything looks off, halt the bot before continuing.
Step 14 of 14
02 / Reference

Endpoints, contracts, links

V2 Exchange contracts (Polygon)

CTF Exchange
0xE111180000d2663C0091e4f400237545B87B996B
Neg Risk Exchange
0xe2222d279d744050d28e00520010520000310F59
Domain version
"2" (was "1")
chainId
137 (mainnet) / 80002 (Amoy)
Auth domain
"1" (unchanged)

SDK packages

Min version
1.0.0
Retired
@polymarket/clob-client · py-clob-client · @polymarket/builder-signing-sdk

Test markets · fee-enabled (use these to verify fee math)

Resolve any condition ID via https://gamma-api.polymarket.com/markets?condition_ids=<id> to get token IDs and metadata.

  • 0xaf5e903876ad42de97e1cf02c2ef8484df69bcfc5541b96a400116557d1e504e
  • 0xe9955a31d76ea97457410f61c9b9f2d27ac6fbd8302ed1849d93133884f4fb3b
  • 0x8e6a263fc5f4dd9433b4ccbc9bb9e0d89f4de8a91d3a82d6b437fe73fd847ea5
  • 0xc7d462e462d8d369aee17c9e3ea8f113166cf9a342a6434a0b2a0f7588dc1bbf
  • 0x0440d08e4f534afa8ad9f616a239a19ebf7f2476bd802b1d54180435fa83463f
  • 0xd048b3e9ccad82c57a4eec953e75794aeb5e6d0e08bdb79b9e01b24e0049f16d
  • 0x88310713845c54e36f29791a1c2dc172b9819b645ba82d5f87560805cd2bb788
  • 0xffcab88076a28795281bb07a082c7003a4ff5671420086cb16ec09dfbf9aea68
  • 0x64a5bcfb0c76a75081c8be49c895a976d506fce8d21aa9d73f1099e502f2fa4e
  • 0x8babe4f4d0eff732d660fa02929a581cb8478ed2a1696c158290b2794d3d7ac0

Test markets · additional liquid markets

Use these for general flow testing when you don't need fee verification.

  • 0xcf8a237df51a51511c4f96bb4390480ad72b898ca8ec7e9a3c16c47c8e5e468a
  • 0x242d251e7e804f79e1f237896b0f5e73caea72375dcb84e5a60d1cd0d2f80ef5
  • 0x9c47ba9e666983bd8d82bfab790509153bf7756c43913f6ef269e33c8955939c
  • 0x221aaa62fed17db56fbc7983f88110a9c34861c3262154ee3315425378e3ae12
  • 0x625d0091f4c647e5497bd9b03f8526bd486d6c339380b8046e4dd5b3373046b7
  • 0xc9b9f89ac915385c8d77edb73872c49e5bf76e510b74b9609e74e7f8d0339df2
  • 0xde9f827cb2d568db7801439693645a941a38fe6feaeb08b86087ad367d991704
  • 0xfd029ab3d6d27b6e1f3480dce858c97fb12e5bebd6fb50be7520102c56ba8ce1
  • 0x894a61a2baa777adf1d03a263a4b2d8faa7e1ebc7bf6694a37701fae8add01d9
  • 0xcd57f3ad3bbbdaa96aacabd35f50c2d6e30f777e4a33876a4ab2dcd9f0b8c170
  • 0xc4b07998e8f9bf6b95f079d6dc0529f3c6f59698d4e168817ad5f99304de6c57
  • 0xe4f4b614a6c2b4ecd8eb700d19c0e6533d3fbd1bc28193b2255394ef74006e6f
  • 0xec181db4470b152493b58229862af3f6335b77cc719f5a0e7ed58c9f9848b992
  • 0x6ec4fec4885df7f3ac46e5d0051beb6d8ac75de6a8481f13f245ff26dcb4b662
  • 0x8f6e71601903224dc29c69b886a63f248c8051be259e7db299850708f1f86dd6
  • 0xfa40b5612a905f16ee42a18979f23fa1bbfcfc365f11d168f2e22bd0159ada77
  • 0x182390641d3b1b47cc64274b9da290efd04221c586651ba190880713da6347d9
03 / Cutover

What happens on April 28

T − 24h

Final dry-run on staging

Re-run Step 11 against clob-v2.polymarket.com with your final code. Confirm WebSocket reconnect logic, builder attribution, and fee math one more time.

T − 30m

Drain & pause

Stop opening new positions. Cancel non-critical resting orders manually so you don't have to recreate them blind. Pause the bot.

~11:00 UTC

Cutover begins

~1 hour of downtime. All remaining open orders are wiped. The V1 SDK / V1-signed clients stop working permanently.

~12:00 UTC

V2 takes over clob.polymarket.com

Flip your POLYMARKET_CLOB_HOST env var to the production URL. Restart the bot. Run Step 13 smoke tests.

T + 1h

Re-place positions, monitor

Recreate any wiped market-making or arbitrage orders using the runbook commands from Step 12. Tail logs for unexpected V2 fields. Confirm builder attribution on the leaderboard.

04 / Help

Stuck on a step? Bring it to Discord.

If your agent gets confused on a step — or if your bot has an unusual structure the prompts don't cover — drop the situation in the PR&R Discord. Other members are migrating their bots in parallel, and we'll workshop the prompt language for whatever's missing.

For protocol questions (contract behavior, order semantics, the builder program) Polymarket's own Discord is the fastest channel: discord.gg/polymarket.

0 / 0 steps complete Reset