demo-wired and shadow-ready.Shadow mode — via the mock CLOB V2 adapter
Shadow mode is the gate between demo-wired and shadow-ready. A bot already runs against the synthetic feed; in shadow mode it also runs against an in-memory mock of the real exchange. Same wire format, same envelope shape, same correlation IDs — with no real capital and no on-chain submission. This page explains exactly how a wired bot moves through that gate using the new @polytraders/mock-clob-v2 package.
Reminder. The mock adapter does not perform real EIP-712 signature verification — it validates the message shape only. It is a behavioural mock for the wire format, not a cryptographic verifier. runtime-live requires the real signed adapter and 24h of clean envelopes against it.
What the mock adapter does
- Validates EIP-712 v2 envelope shape. Domain must be
{name:"CTFExchangeV2",version:"2",chainId:137,verifyingContract:0x4D97...6045}.primaryTypemust beOrder. The 13 message fields (salt, maker, signer, taker, tokenId, makerAmount, takerAmount, expiration, nonce, feeRateBps, side, signatureType, builderCode) must be present and well-formed. Anything else →EXEC_BAD_ENVELOPE. - Runs an in-memory order book. Bids descending, asks ascending. Supports GTC, IOC, FOK. Deterministic across runs given the same seed and submission order.
- Emits ReportEnvelopes. Every fill, partial, rest, cancel and reject becomes a
ReportEnvelopewith botIdexec.mockclobv2, configVersionmock-v0, and a correlation ID linking it back to the originatingOrderIntent. - Two transports, one API. Use it as an HTTP server (
POST /order,POST /cancel,GET /book/:marketId,GET /fills,GET /health) or as an in-process JS handle — the runner core stays HTTP-agnostic.
Reason codes the adapter emits
All match ^EXEC_[A-Z0-9_]+$ as required by the ReasonCode registry:
EXEC_FILL,EXEC_RESTING,EXEC_CANCELLED— lifecycle events.EXEC_REJECTED,EXEC_BAD_ENVELOPE,EXEC_BAD_INTENT,EXEC_BAD_SIZE,EXEC_BAD_PRICE,EXEC_BAD_SIDE,EXEC_BAD_TIF— rejections.EXEC_NO_LIQUIDITY,EXEC_FOK_NOT_FULLY_FILLABLE,EXEC_UNKNOWN_MARKET,EXEC_UNKNOWN_ORDER,EXEC_ORDER_TERMINAL,EXEC_UNKNOWN_ROUTE— book / state errors.EXEC_BAD_REQUEST,EXEC_INTERNAL— transport errors.
The promotion path: demo-wired → shadow-ready
- Pre-flight. The bot is already
demo-wired: passesnpm test, has a fixture pack, runs in the synthetic backtest. If any of these are missing, this page does not apply yet. - Seed the mock book. Call
POST /admin/seed(orclient.seedMarket()) with deterministic bid/ask ladders for every market the bot will touch. Seed data is part of the test fixture, not improvised. - Run intents through the bot. Drive at least 100 representative
OrderIntents through the bot → runner → mock adapter. Mix of fills, partials, rests, cancels, and intentional rejects (bad envelope, FOK that cannot fill, halted market). - Capture envelopes. Every emitted
ReportEnvelopeis appended to the bot's audit log with the correlation ID. The set of envelopes is the artefact reviewers grade. - Determinism check. Re-run the same intent stream with the same seed. Envelope sequence must be byte-identical except for wall-clock timestamps. The
determinismtest inpackages/polytraders-mock-clob-v2/tests/verify.jsdemonstrates the contract. - 24h soak. Run the bot continuously for 24 hours against a synthetic market generator that drives the mock book through calm, volatile, drift, fat-tail and resolution regimes. Zero unexpected reason codes. Zero envelope-shape violations.
- Promote. When all of the above are green, flip the bot's row in the Plan from
demo-wiredtoshadow-ready.runtime-liverequires the real adapter against a real testnet — this page does not cover that.
What "shadow mode" means in this guide
- No real signing. The mock adapter accepts any 65-byte signature whose hex is well-formed. The runner still constructs the full envelope as it would in production — the only difference is that the verifying contract never actually verifies.
- No capital. Fills are bookkeeping inside the in-memory book. There is no token transfer.
- No external network. The HTTP server binds to
127.0.0.1only by default. The in-process transport bypasses the loopback entirely. - Same correlation IDs as production. A bot promoted to
runtime-liveswaps the adapter URL but keeps the same intent and envelope plumbing. That equivalence is the whole point of shadow mode.
Where this lives in the codebase
packages/polytraders-mock-clob-v2/src/server.js— HTTP server (default port 8787, loopback only).packages/polytraders-mock-clob-v2/src/orderbook.js— in-memory deterministic CLOB.packages/polytraders-mock-clob-v2/src/eip712.js— envelope builder + shape validator.packages/polytraders-mock-clob-v2/src/envelopes.js— ReportEnvelope emitters.packages/polytraders-mock-clob-v2/src/client.js— adapter client (HTTP or in-process; runner imports this, never rawfetch).packages/polytraders-mock-clob-v2/tests/verify.js— 10 tests including100-intents-shadowanddeterminism.
What this is not
- Not a forecasting tool. Synthetic backtests against the mock are not predictions of live performance.
- Not a security audit. Shape validation does not detect signature forgery. Cryptographic verification is the real adapter's job.
- Not a license to ship.
shadow-readystill requires a fixture pack, a CI green light on all four readiness scores, and 24h of clean envelopes.production-liverequires capital authorisation and change control on top of all of that.