> ## Documentation Index
> Fetch the complete documentation index at: https://docs.predexon.com/llms.txt
> Use this file to discover all available pages before exploring further.

# V2 Migration Guide

> What changed in the WebSocket API for Polymarket V2 - deployed 2026-04-19

<Tip>
  **Zero code changes required for most clients.** All existing subscriptions, filters, and event fields continue to work unchanged. V2 adds new fields and new event types alongside V1 - nothing was removed or renamed.
</Tip>

## TL;DR

Polymarket is migrating from V1 contracts (USDC.e collateral) to V2 contracts (pUSD collateral). The WebSocket service now handles both - V1 and V2 events flow through the same channels and subscription semantics.

Additive changes:

1. New `version` field on `order_filled`, activity, and lifecycle events (`1` or `2`)
2. New `builder` and `metadata` fields on V2 `order_filled` events
3. New `collateral` channel for pUSD deposit/withdrawal events
4. New `condition_prepared` event type on the lifecycle channel (with derived token IDs for binary markets)
5. New `conversion` event type on the activity channel (NegRisk cross-condition conversions)

Two things to be aware of - neither affects most clients:

* **`ActivityAlert.condition_id` is now nullable** - `null` only for the new `conversion` event type. Clients with typed schemas (`condition_id: string`) should widen to `string | null` if they want to consume conversion events. Clients that filter by `event_type` in `{"split", "merge", "redeem"}` are unaffected.
* **Pending-tx access is now Dev+ gated** - Free-tier clients passing `filters.status: "all"` or `"pending"` receive `PLAN_REQUIRED`. Free-tier clients on the default (`"confirmed"`) continue to work unchanged.

***

## Breaking Change: `token_registered` Only Fires for V1

<Warning>
  V2 exchanges no longer emit `TokenRegistered`. If you use `token_registered` as your "new market" signal, you'll miss V2 markets.

  **Migration:** subscribe to the new `condition_prepared` event type on the lifecycle channel. It fires for both V1 and V2 markets - it's the upstream signal from the shared `ConditionalTokens` contract.
</Warning>

| Event                  | V1 Markets | V2 Markets            |
| ---------------------- | ---------- | --------------------- |
| `token_registered`     | Yes        | **No longer emitted** |
| `condition_prepared`   | Yes (new)  | Yes (new)             |
| `condition_resolution` | Yes        | Yes                   |

The `condition_prepared` event fires earlier than `token_registered`. For **binary markets** (most Polymarket markets), the service derives token IDs deterministically from `condition_id` + `is_neg_risk` and includes them in the `tokens` array - no extra API call needed. Token labels are placeholders (`"Yes"` / `"No"`); resolve canonical labels from Gamma/CLOB if needed. Non-binary markets have `tokens: null`.

***

## New Fields on Existing Events

### Trade events (`order_filled`)

V1 fills are byte-identical to pre-migration **plus** a single new `version` field:

```json theme={null}
{
  "event_type": "order_filled",
  "side": "BUY",
  "price": 0.61,
  "shares_normalized": 2.564101,
  "status": "confirmed",
  "version": 1
}
```

V2 fills add `builder` and `metadata` (bytes32 hex, zero until Polymarket populates them):

```json theme={null}
{
  "event_type": "order_filled",
  "side": "BUY",
  "price": 0.45,
  "shares_normalized": 1.81818,
  "status": "confirmed",
  "version": 2,
  "builder": "0x0000...0000",
  "metadata": "0x0000...0000"
}
```

**V2 fee semantics:**

* **No `fee_refund` events** on V2 - V2 has no refund flow. The net fee is emitted directly on `order_filled` at match time.
* V2 uses separate taker and per-maker fee amounts. In practice makers typically see `fee: 0` today (operators haven't populated maker fees), but the protocol supports non-zero maker fees via `makerFeeAmounts[]`.
* V1 `fee_refund` behavior is unchanged.

### Lifecycle events

Now carry a `version` field (`1` or `2`) indicating which contracts emitted them. All other fields unchanged.

### Activity events

Now carry a `version` field (`1` for split/merge/redeem, `2` for the new `conversion` event type).

### New Activity Event: `conversion`

NegRisk cross-condition conversions (user converts "No" positions back to collateral across a NegRisk event).

```json theme={null}
{
  "event_type": "conversion",
  "user": "0xbc5403011df159368c4bc716e07bcf858dfe2eec",
  "condition_id": null,
  "amount": 13074500,
  "amount_normalized": 13.0745,
  "tx_hash": "0x...",
  "timestamp": 1776824366,
  "version": 2,
  "neg_risk_event_id": "0x6375f96fe3910d68...",
  "index_set": "320"
}
```

* `condition_id` is always `null` (conversion spans multiple conditions).
* `neg_risk_event_id`: NegRiskAdapter's marketId identifying the cluster of related binary questions.
* `index_set`: `uint256` bitmap (as decimal string) of which question indices were converted.
* See [Activity Events](/websocket/activity#conversion) for full details.

***

## New Channel: `collateral`

V2 replaces USDC.e with pUSD as the trading collateral. When users deposit/withdraw, the pUSD contract emits `Wrapped` / `Unwrapped` events. These flow through a new opt-in `collateral` channel.

**Subscribe:**

```json theme={null}
{
  "action": "subscribe",
  "platform": "polymarket",
  "version": 1,
  "type": "collateral",
  "filters": { "users": ["0x..."] }
}
```

Accepts `users` filter or wildcard. Does **not** accept `condition_ids`, `market_slugs`, or `token_ids` - collateral flow isn't tied to a market.

**Event shape:**

```json theme={null}
{
  "event_type": "polyusd_wrapped",
  "user": "0x3b27d0fb...",
  "asset": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
  "amount": 45000000,
  "amount_normalized": 45,
  "tx_hash": "0x...",
  "timestamp": 1776638042,
  "version": 2
}
```

* `event_type`: `"polyusd_wrapped"` (deposit: underlying → pUSD) or `"polyusd_unwrapped"` (withdrawal: pUSD → underlying)
* `user`: end-recipient of the operation
* `asset`: underlying ERC-20 address (typically USDC.e)
* `amount` / `amount_normalized`: 6-decimal (same as USDC)

<Note>
  Activity events (splits, merges, redeems) are **not** mixed into the collateral channel. Subscribe to both if you need full wallet flow.
</Note>

See [Collateral Events](/websocket/collateral) for the full event reference.

***

## Accepted Channel Types

| `type`       | Status                                              |
| ------------ | --------------------------------------------------- |
| `orders`     | Unchanged                                           |
| `activity`   | Unchanged - pUSD events **not** mixed in            |
| `lifecycle`  | Unchanged filters; may deliver `condition_prepared` |
| `oracle`     | Unchanged                                           |
| `orderbook`  | Unchanged                                           |
| `collateral` | **New**                                             |

***

## Contract Addresses

| Role                      | Address                                      |
| ------------------------- | -------------------------------------------- |
| V2 CTF Exchange           | `0xe111180000d2663c0091e4f400237545b87b996b` |
| V2 NegRisk Exchange A     | `0xe2222d279d744050d28e00520010520000310f59` |
| V2 NegRisk Exchange B     | `0xe2222d002000ba0053cef3375333610f64600036` |
| PolyUSD (pUSD)            | `0xc011a7e12a19f7b1f670d46f03b03f3342e82dfb` |
| ConditionalTokens (V1+V2) | `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045` |

***

## Backwards-Compatibility Guarantees

* **No field removed, no field renamed, no filter semantic changed.**
* V1 clients that don't parse `version`, `builder`, or `metadata` will ignore them - same JSON shape as before plus extra keys.
* Clients with **strict JSON schemas** that reject unknown properties need a one-line schema update (allow additional properties, or add the new optional fields).
* Clients with a **closed enum** for `event_type` should add `"condition_prepared"`, `"polyusd_wrapped"`, `"polyusd_unwrapped"` if they want to surface those.

***

## Recommended Migration Steps

<Steps>
  <Step title="Update your event handlers (optional)">
    Read the new `version` field on trade/activity/lifecycle events if you want to branch on V1 vs V2 behavior.
  </Step>

  <Step title="Fix token discovery (required if you relied on token_registered)">
    If you used `token_registered` as your "new market" signal, add a handler for `condition_prepared`. Query Gamma or CLOB by `condition_id` to resolve token IDs.
  </Step>

  <Step title="Subscribe to collateral (optional)">
    If you track wallet deposit/withdrawal flow, subscribe to the new `collateral` channel with `type: "collateral"`.
  </Step>

  <Step title="Relax strict schema validation (if applicable)">
    If your JSON parser rejects unknown keys, add `version`, `builder`, `metadata` as optional fields - or allow additional properties.
  </Step>
</Steps>
