> Fetch the complete documentation index at: https://docs.polymarket.com/llms.txt
> Use this file to discover all available pages before exploring further.
# Migrating to CLOB V2
> A complete guide to upgrading your integration to Polymarket's CLOB V2 — new contracts, new backend, new collateral token, and a simpler builder program.
Polymarket has shipped a coordinated upgrade of its entire trading infrastructure: **new Exchange contracts**, a **rewritten CLOB backend**, and a **new collateral token** (Polymarket USD, or pUSD). This guide walks you through everything you need to migrate from CLOB V1 to CLOB V2.
**CLOB V2 is live as of April 28, 2026.** Legacy V1 SDKs and V1-signed orders are no longer supported on production. Upgrade to the V2 SDK or update your raw order signing before submitting orders to `https://clob.polymarket.com`.
**Production URL:** CLOB V2 now runs at `https://clob.polymarket.com`. The pre-cutover `https://clob-v2.polymarket.com` testing host is no longer the integration target for production clients.
| Builder auth | `POLY_BUILDER_*` HMAC headers + [`builder-signing-sdk`](https://github.com/Polymarket/builder-signing-sdk) | A single `builderCode` field on the order |
| EIP-712 domain version | `"1"` | `"2"` (exchange only — API auth unchanged) |
| Exchange `verifyingContract` (raw API signers) | V1 addresses | V2 addresses — see [Contracts](/resources/contracts) |
| Raw API order signing | V1 Order type | Updated Order type — [see API users section](#for-api-users) |
**If you're on the latest SDK,** most of this is handled automatically. If you're signing orders manually, update the EIP-712 domain, order fields, and contract addresses described below.
Install the V2 SDK: [`@polymarket/clob-client-v2`](https://www.npmjs.com/package/@polymarket/clob-client-v2) (TypeScript) or [`py-clob-client-v2`](https://pypi.org/project/py-clob-client-v2/) (Python). Don't keep using the old `clob-client` / `py-clob-client` packages — those only work against V1 and no longer function against production.
**Open orders from before the CLOB V2 cutover were wiped.** If you had resting V1-era orders, they did not migrate and must be re-created with V2 order signing.
The onchain exchange has been rewritten from the ground up.
* Solidity upgraded from **0.8.15 → 0.8.30**, with Solady replacing OpenZeppelin for gas savings.
* Order struct simplified: `nonce`, `feeRateBps`, and `taker` removed; `timestamp`, `metadata`, `builder` added.
* EIP-712 exchange domain version bumped from `"1"` to `"2"`.
* Fees collected onchain at match time (no longer embedded in the signed order).
* Onchain cancel replaced with operator-controlled `pauseUser` / `unpauseUser`.
* Batched mint/merge operations for gas efficiency.
See [Contracts](/resources/contracts) for V2 addresses.
### 2. Rewritten CLOB backend
The order manager, ledger, executor, balance checker, and tracker are all new services. From an integrator's perspective:
* **Nonce system removed.** Order uniqueness now comes from `timestamp` (milliseconds). You no longer track nonces.
* **New fee model.** Platform fees are dynamic per market and queryable via `getClobMarketInfo()`.
* **Builder codes** enable integrator attribution and revenue sharing, replacing the old HMAC-header flow.
### 3. New collateral token
Polymarket is migrating from **USDC.e** to **pUSD** (Polymarket USD), a standard ERC-20 on Polygon backed by USDC. Backing is enforced onchain by the smart contract.
* For users trading on polymarket.com, the frontend handles wrapping automatically with a one-time approval.
* Power users and API-only traders wrap their USDC.e into pUSD via the Collateral Onramp contract's `wrap()` function.
***
## For API users
If you sign and post orders directly (without the SDK), here's what changes in the wire protocol. SDK users can skip this section — the client handles it.
### EIP-712 domain
The Exchange domain version bumps to `"2"` and the `verifyingContract` moves to the V2 Exchange.
* **`timestamp`** — order creation time in milliseconds. Replaces `nonce` for per-address uniqueness (not an expiration).
* **`metadata`** — bytes32.
* **`builder`** — bytes32. Zero unless you're attaching a builder code.
`side` is encoded as `uint8` in the signing payload (`0` = BUY, `1` = SELL), even though the wire body uses the string `"BUY"` / `"SELL"`. No change from V1.
The API auth headers are unchanged. Builder attribution moves into the signed `builder` field on the order, so the `POLY_BUILDER_*` HMAC headers are gone.
```yaml theme={null}
POLY_ADDRESS: 0x...
POLY_SIGNATURE: 0x...
POLY_TIMESTAMP: 1713398400
POLY_API_KEY: ...
POLY_PASSPHRASE: ...
POLY_BUILDER_API_KEY: ... # [!code --]
POLY_BUILDER_SECRET: ... # [!code --]
POLY_BUILDER_PASSPHRASE: ... # [!code --]
POLY_BUILDER_SIGNATURE: 0x... # [!code --]
```
***
## SDK Migration
### Install
CLOB V2 ships under new package names. Install them directly — don't keep using the old `clob-client` / `py-clob-client` packages.
**What's next.** We're planning a unified SDK that folds Gamma, Data, and CLOB into a single package. Future releases will converge there — for now, `clob-client-v2` / `py-clob-client-v2` are the V2 CLOB clients.
</Note>
### Constructor: positional args → options object
The single most visible change. `chainId` is renamed to `chain`. `tickSizeTtlMs` is no longer configurable.
<CodeGroup>
```typescript Before (V1) theme={null}
const client = new ClobClient(
host,
chainId,
signer,
creds,
signatureType,
funderAddress,
useServerTime,
builderConfig,
getSigner,
retryOnError,
tickSizeTtlMs, // ← removed in V2
throwOnError,
);
```
```typescript After (V2) theme={null}
const client = new ClobClient({
host,
chain: chainId, // ← renamed from chainId
signer,
creds,
signatureType,
funderAddress,
useServerTime,
builderConfig, // shape changed — see Builder Program below
getSigner,
retryOnError,
throwOnError,
});
```
</CodeGroup>
<Tip>
The only mental shift: wrap args in `{}` and rename `chainId` → `chain`. Everything else is the same.
</Tip>
### Order creation
Three fields are no longer user-settable: `feeRateBps`, `nonce`, `taker`. One new optional field: `builderCode`.
<CodeGroup>
```typescript Before (V1) theme={null}
const order: UserOrder = {
tokenID: "0x123...",
price: 0.55,
size: 100,
side: Side.BUY,
feeRateBps: 100, // ← removed
nonce: 12345, // ← removed
taker: "0xabc...", // ← removed
expiration: 1714000000,
};
```
```typescript After (V2) theme={null}
const order: UserOrderV2 = {
tokenID: "0x123...",
price: 0.55,
size: 100,
side: Side.BUY,
expiration: 1714000000,
builderCode: "0x...", // optional — your builder code
};
```
</CodeGroup>
**Market orders** follow the same pattern and add an optional `userUSDCBalance` field so the SDK can calculate fee-adjusted fill amounts:
If you were calculating fees manually in your integration, you can now rely on the SDK. Pass `userUSDCBalance` on market buy orders to get accurate fill amounts after fees.
***
## Builder Program
V2 replaces the old builder authentication flow (HMAC headers + separate signing SDK) with a native **builder code** attached directly to each order.
* A single `builderCode` (bytes32) from your [Builder Profile](https://polymarket.com/settings?tab=builder)
* Attach it per-order via the `builderCode` field, **or** pass it once at construction so every order inherits it
<Note>
`BuilderConfig` still exists, but its shape changed. In V1 it wrapped HMAC credentials from `@polymarket/builder-signing-sdk`. In V2 it's just `{ builderCode: string }`.
**Your builder API key isn't retired.** The HMAC-based builder API key is still used to authenticate with the [Relayer](/trading/gasless) for gasless transactions. Only the order-signing flow moves to the `builderCode` field — your relayer integration keeps the same credentials.
// Every order posted by this client now carries your builder code.
await client.createAndPostOrder(
{
tokenID: "0x...",
price: 0.55,
size: 100,
side: Side.BUY,
},
{ tickSize: "0.01", negRisk: false },
);
```
</CodeGroup>
***
## Collateral token: USDC.e → pUSD
Polymarket USD (pUSD) replaces USDC.e as the collateral token. pUSD is a standard ERC-20 on Polygon backed by USDC, with backing enforced onchain by the smart contract. The permissionless Collateral Onramp accepts USDC.e.
* **For users on polymarket.com:** the UI handles wrapping automatically.
* **For API-only traders:** wrap USDC.e into pUSD via the Collateral Onramp's `wrap()` function. See the [pUSD page](/concepts/pusd) for full examples and [Contracts](/resources/contracts) for addresses.
The markets below were used for pre-cutover testing on `clob-v2.polymarket.com` and are retained for migration reference. For live testing, resolve current active markets into token IDs and metadata with [`gamma-api.polymarket.com/markets?condition_ids=<id>`](https://gamma-api.polymarket.com/markets), then submit small orders to production CLOB V2 at `https://clob.polymarket.com`.
Follow [Discord](https://discord.gg/polymarket), Telegram, and [status.polymarket.com](https://status.polymarket.com) for operational updates and incident notices.
<Accordion title="Do I need to migrate my USDC.e to pUSD manually?">
If you're trading through polymarket.com, no — the UI handles wrapping automatically with a one-time approval. If you're API-only, you'll need to call `wrap()` on the Collateral Onramp contract.
</Accordion>
<Accordion title="Is the builder code a secret?">
No. Builder codes are **public identifiers** — they appear onchain in the `builder` field of every attributed order. Only you control which orders include your code, so keep it scoped to apps you own.
</Accordion>
<Accordion title="I calculate fees manually. What do I change?">
Remove the manual calculation. Use `getClobMarketInfo(conditionID)` to query fee parameters (`fd.r`, `fd.e`, `fd.to`), and rely on the SDK to handle fee-adjusted amounts. Pass `userUSDCBalance` on market buy orders for accurate fill math.
</Accordion>
<Accordion title="Are WebSocket URLs or payloads changing?">
WebSocket URLs are unchanged. Most message payloads are unchanged. The `fee_rate_bps` field on `last_trade_price` events continues to reflect the fee actually charged on the trade.