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

# Combo Stream

> Real-time Polymarket combo trades from pending calldata through receipt confirmation.

PolyNode streams Polymarket combo activity from on-chain combo transactions. The stream follows the same lifecycle as standard settlements: pending events come from mempool calldata, then confirmation events come from on-chain receipts.

<Info>
  This is the on-chain combo stream. Polymarket's RFQ gateway is an off-chain maker/quoter channel. Use this stream to monitor combo executions, fills, confirmations, and enriched lifecycle actions as they hit Polygon.
</Info>

## Subscribe

```json theme={null}
{
  "action": "subscribe",
  "type": "combos"
}
```

The default `combos` preset emits:

| Event                 | Timing              | Purpose                                                                             |
| --------------------- | ------------------- | ----------------------------------------------------------------------------------- |
| `combo_execution`     | Pending / mempool   | Combo order execution decoded before confirmation.                                  |
| `combo_status_update` | Confirmed / receipt | Confirmation, exact receipt fills, fees, transfers, lifecycle details, and latency. |

Every emitted combo includes its decoded structural legs. Cached market titles,
slugs, images, outcomes, and token IDs are added when available, but a cache
miss never makes a valid pending trade wait on Redis or an external API.

## Trade model

`combo_execution` is intentionally shaped like the ordinary settlement stream:

* Top-level `taker_*` fields describe the taker order.
* `trades[]` contains every signed order from that order maker's perspective.
* `size` is always shares; `amount_usdc` is always dollars.
* `price` is the actual fill price, including multi-maker price improvement.
* `order_hash` is the ExchangeV3 EIP-712 hash computed directly from calldata.

After confirmation, the same fills are queryable through
[`GET /v3/wallets/{address}/combos/trades`](/data/combos/wallet-trades).
The static API adds block and log coordinates; the WebSocket adds
pre-confirmation delivery, order hashes, and explicit dollar amounts.

## Full combo surface

Advanced users can explicitly request lifecycle and approval events:

```json theme={null}
{
  "action": "subscribe",
  "type": "combos",
  "filters": {
    "event_types": [
      "combo_execution",
      "combo_status_update",
      "combo_lifecycle",
      "combo_approval"
    ]
  }
}
```

Lifecycle events are emitted when their on-chain structure is complete.
AutoRedeemer redemption confirmations can also be emitted when they are
condition-only, because the receipt provides the user wallet, condition ID,
exact payout, normalized payout, and log index without requiring a follow-up
lookup.

## Redemption routing

| What you need          | Subscription                                                                                             | What it contains                                                                                                                    |
| ---------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| Every redemption       | `{"action":"subscribe","type":"redemptions"}`                                                            | Direct Conditional Tokens redemptions plus Binary, NegRisk, and generic combo AutoRedeemer payouts, all normalized as `redemption`. |
| Combo redemptions only | `{"action":"subscribe","type":"combos","filters":{"event_types":["combo_lifecycle"],"action":"Redeem"}}` | Combo-native redeem lifecycle events only.                                                                                          |

The comprehensive `redemptions` stream identifies bridged AutoRedeemer events with `data.source`: `AutoRedeemer.BinaryRedemption`, `AutoRedeemer.NegRiskRedemption`, or `AutoRedeemer.Redemption`. A direct Conditional Tokens redemption has no AutoRedeemer source.

If you subscribe to both surfaces, a generic combo AutoRedeemer payout can appear once as `combo_lifecycle` and once as normalized `redemption`. These are two representations of the same on-chain log; correlate or deduplicate them with `tx_hash` and `log_index`.

## Covered on-chain surface

The combo stream covers the Polymarket on-chain contracts used for combo execution and position lifecycle activity:

| Contract            | Address                                      | Surface                                                                                        |
| ------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| ExchangeV3          | `0xe3333700cA9d93003F00f0F71f8515005F6c00Aa` | Combo order execution and confirmation.                                                        |
| PositionManager     | `0x006F54F7f9A22e0000CC2AB60031000000ae9fEF` | Position transfers and approvals.                                                              |
| BinaryModule        | `0x1000008dD9001B968442c1000017eaE6E0dA00Ba` | Split, merge, redeem, migration, and result lifecycle calls.                                   |
| NegRiskModule       | `0x200000900045e3B6259600682756002200028933` | Negative-risk split, merge, redeem, convert, migration, and result calls.                      |
| CombinatorialModule | `0x30000034706C7d8e12009DAB006Be20000c031A8` | Prepare, split, merge, extract, inject, wrap, unwrap, compress, redeem, and basket conversion. |
| Router              | `0x12121212006e4CD160D18e3f00711DA5c3372600` | Router split, merge, redeem, horizontal split/merge, and convert.                              |
| AutoRedeemer        | `0xa1200000d0002264C9a1698e001292D00E1b00af` | Auto redeem calls and confirmations.                                                           |

PositionManager approvals for ExchangeV3 and AutoRedeemer are available through `combo_approval` when requested explicitly. Collateral ERC20 allowance approvals are not part of the default combo stream.

## Example

Install `ws`, save the example as `combos.mjs`, and run it with Node.js:

```bash theme={null}
npm install ws
node combos.mjs
```

```javascript theme={null}
import WebSocket from "ws";

const ws = new WebSocket("wss://ws.polynode.dev/ws?key=pn_live_YOUR_KEY");
const pending = new Map();
const PENDING_TTL_MS = 15 * 60 * 1000;

const cleanup = setInterval(() => {
  const cutoff = Date.now() - PENDING_TTL_MS;
  for (const [txHash, item] of pending) {
    if (item.seenAt < cutoff) pending.delete(txHash);
  }
}, 60_000);
cleanup.unref();

ws.on("open", () => {
  ws.send(JSON.stringify({ action: "subscribe", type: "combos" }));
});

ws.on("message", (raw) => {
  const msg = JSON.parse(raw);

  for (const event of msg.events ?? [msg]) {
    if (event.type === "combo_execution") {
      pending.set(event.data.tx_hash, { data: event.data, seenAt: Date.now() });
      console.log("pending combo", {
        tx_hash: event.data.tx_hash,
        taker: event.data.taker_wallet,
        side: event.data.taker_side,
        outcome: event.data.outcome,
        price: event.data.taker_price,
        shares: event.data.taker_size,
        amount_usdc: event.data.amount_usdc,
        legs: event.data.legs.map((leg) => ({
          outcome: leg.leg_outcome_label,
          market: leg.market?.title ?? leg.condition_id
        })),
        trades: event.data.trades.map((trade) => ({
          order_hash: trade.order_hash,
          maker: trade.maker,
          side: trade.side,
          outcome: trade.outcome,
          price: trade.price,
          shares: trade.size,
          amount_usdc: trade.amount_usdc
        }))
      });
    }

    if (event.type === "combo_status_update") {
      const original = pending.get(event.data.tx_hash)?.data;
      if (["CONFIRMED", "FAILED"].includes(event.data.execution_status)) {
        pending.delete(event.data.tx_hash);
      }
      console.log("combo status", event.data.tx_hash, {
        execution_status: event.data.execution_status,
        latency_ms: event.data.latency_ms,
        pending_seen: Boolean(original),
        fills: event.data.confirmed_fills?.length ?? 0
      });
    }
  }
});
```

## Filters

Combo subscriptions support the standard WebSocket filters plus combo-specific identifiers:

```json theme={null}
{
  "action": "subscribe",
  "type": "combos",
  "filters": {
    "wallets": ["0xMakerOrTaker..."],
    "leg_position_ids": ["123456789..."],
    "tokens": ["123456789..."],
    "combo_condition_ids": ["0xcombo..."],
    "condition_ids": ["0xleg_or_combo_condition..."],
    "event_ids": ["0xevent..."],
    "module_ids": [3],
    "side": "YES",
    "direction": "BUY",
    "status": "confirmed",
    "min_size": 100
  }
}
```

`tokens` and `leg_position_ids` both match combo position IDs and leg position IDs. Use `leg_position_ids` when you want the filter to be self-documenting.

For combo subscriptions, `side` means the combo outcome (`YES` or `NO`), while
`direction` means the requester's order direction (`BUY` or `SELL`). `status:
"confirmed"` selects receipt-backed `combo_status_update` events, including
both successful and reverted receipts; use `execution_status` to distinguish
`CONFIRMED` from `FAILED`. `min_size` is a minimum USDC value.

To receive only combo lifecycle redemptions:

```json theme={null}
{
  "action": "subscribe",
  "type": "combos",
  "filters": {
    "event_types": ["combo_lifecycle"],
    "action": "Redeem"
  }
}
```

## Event reference

* [`combo_execution`](/websocket/events/combo-execution)
* [`combo_status_update`](/websocket/events/combo-status-update)
* [`combo_lifecycle`](/websocket/events/combo-lifecycle)
* [`combo_approval`](/websocket/events/combo-approval)
