pn1 means PolyNode orderbook integrity protocol, version 1. You only send integrity=1; the protocol name appears in server messages so the checksum bytes are unambiguous.
PN1 verifies the continuity and resulting state of PolyNode-delivered depth. It is not a Polymarket sequence number, does not call Polymarket while processing a frame, and is not a cryptographic signature. PolyNode’s separate upstream accuracy monitoring remains out of band and adds no stream latency.
The client rule
For each token:- Accept a message with
anchor: trueas a new baseline. Replace the entire local book, compute PN1, compare the checksum, and storeseq. - For the next depth update, require
prev_seqto equal the stored sequence. - Apply every changed level. A size of
"0"removes that price; every other size is the new absolute size. - Recompute PN1 and require it to equal the message checksum.
- Store
seqonly after both checks pass. - On any failure, stop using that token and request a new anchor.
resyncing, a book_snapshot anchor, then snapshots_done with "reason":"resync". If one is already in flight, resync_in_progress tells you to keep the token gated and retry after retry_after_ms. If an acknowledged anchor cannot complete, the server fails closed with integrity_backpressure or integrity_internal_reset and closes the socket; reconnect for fresh anchors.
Wire format
The subscribe acknowledgement is queued before any anchors:price_change is token-scoped: it has a top-level asset_id and exactly one row in assets. This keeps verification identical for every depth frame. A non-depth price_change has no integrity object. last_trade_price never changes depth, never advances seq, and has no checksum.
The normal stream (integrity omitted or integrity=0) keeps its existing payloads. Integrity mode does not support markets:["*"]; pass an explicit list of up to 500 resolved token IDs. Existing account limits still apply if they are lower. The process-wide launch limit is 5,000 unique PN1 tokens.
PN1 checksum
PN1 stores one canonical size for each(side, price) level. B means bid and A means ask.
Canonical decimal rules:
- Accept only a non-negative base-10 string with ASCII digits and at most one decimal point.
- Add a missing integer zero, remove leading integer zeroes, and remove trailing fractional zeroes.
- Remove an empty fraction and decimal point. Every representation of zero becomes
0. - A canonical size of
0removes the level.
.50, 0.500, and 000.5000 are all 0.5; 01.000 is 1.
For every active level:
[{"price":"...","size":"..."}] bid and ask arrays as a snapshot. If your local book uses maps, convert each map to those rows before calling the function.
On a live frame, compute the checksum after applying that frame and compare it to
integrity.checksum. No REST request, Rust process, SDK, or server-side helper is involved on the client.
Copyable checksum implementations
Choose the language your orderbook client already uses. Node.js and Python include both full-recompute and incremental forms; Go and Rust show the shortest full-recompute form. Every block is executable and contains the same fixed PN1 vectors.Node.js checksum reference
Node.js checksum reference
This implementation uses only the Node.js standard library.
Python checksum reference
Python checksum reference
This implementation uses only the Python standard library.
Go full-recompute reference
Go full-recompute reference
Go’s standard library supplies SHA-256. This version favors clarity and recomputes from the complete local book.
Rust full-recompute reference
Rust full-recompute reference
Rust’s standard library does not include SHA-256, so add
sha2 = "0.10" to Cargo.toml. No PolyNode SDK is required.Applying frames
Use onePN1Book and stored sequence per token, per connection. This block uses the Node.js PN1Book above and handles standalone anchors, batched updates, targeted resync, terminal errors, and optional compress=zlib frames:
close handler as the final reconnect trigger. A new connection and subscription establish new anchors; never reuse sequence state across sockets.

