Skip to main content

Monaco Protocol SDK v1.0.38

This is an additive release — no breaking type changes. It brings the typed WebSocket surface up to par with the account APIs: dedicated sdk.ws.positions and sdk.ws.liquidations streams, and reconnect/resync callbacks you can now wire straight into the SDK config instead of dropping to the low-level factory. It also makes postOnly a durable, readable property of an order and gives single-order crossing rejections the same structured error code the batch endpoints already carried, adds an order_id filter to the gRPC user-trades history, and ships three PitPass React hooks. Two changes need a closer look before you upgrade: a set of collateral withdrawal-gate corrections that make withdrawableCollateral report a number a transfer-out will actually accept (expect a smaller figure than before for any account carrying PnL, funding, or an unrealized gain), and a WebSocket order-event field-name correction — read tradingPair, filledQuantity, and fee on order events, not the never-populated symbol / executionQuantity / makerFee.

Added

Typed positions and liquidations WebSocket streams

sdk.ws.positions(handler, tradingPairId?) and sdk.ws.liquidations(handler, tradingPairId?) (@0xmonaco/core) subscribe to the authenticated positions and liquidations channels, parse each raw server frame into a typed PositionEvent / LiquidationEvent (@0xmonaco/types), and return an unsubscribe function — mirroring the existing orders / balances / movements helpers instead of the generic subscribe<T>. Both require the signed session-key handshake. Positions use a conflating stream — a lagging subscriber is fast-forwarded to the latest state, so there is no gap signal; treat updatedAt as monotonic per positionId. Liquidations use a sequential stream and shed a slow subscriber with close code 1013 — treat a 1013 close as the gap signal and resync from a fresh snapshot. Parsing is lenient on the free-form / enum string fields (side, status, reason, accountState) so an unexpected server value never drops a risk-critical event. See WebSockets.

WebSocket reconnect/resync configuration on the SDK config

SDKConfig gains an optional ws object (ws?: MonacoWebSocketConfig): the onStatusChange / onResync callbacks and the reconnect knobs (autoReconnect, maxReconnectAttempts, reconnectBaseDelayMs, connectionTimeoutMs) are now threaded into the WebSocket client the SDK builds internally. Previously these were reachable only through the low-level createMonacoWebSocket factory, so an app using the SDK could not observe connection status or drive snapshot resyncs. Pass them in the SDK config — new MonacoSDK({ ..., ws: { onStatusChange, onResync } }) — and onStatusChange fires on connect/disconnect/reconnect while onResync fires after an automatic reconnect resumes the live stream (with slowClient: true for a 1013 slow-client shed). The session keypair is intentionally not part of this surface — it continues to flow from login. @0xmonaco/types adds MonacoWebSocketConfig, StatusHandler, ResyncHandler, and WebSocketResyncInfo.

postOnly on order reads and a structured single-order crossing code

GetOrderResponse — order detail and every order-list item — gains an optional postOnly boolean (wire post_only; gRPC field tag 42) recording whether the order was placed post-only. Like reduceOnly, it is absent (not false) when the order was not placed post-only, including orders placed before the flag was persisted; postOnly is now a durable property of the order rather than an admission-time-only input. Exposed on sdk.trading.getOrder / getOrders (@0xmonaco/core), REST GET /api/v1/orders and GET /api/v1/orders/{order_id}, and gRPC OrdersService.GetOrder / ListOrders. Single-order rejections that originate in the matching engine now carry the engine’s stable code, so single-order callers get the same structured contract the batch endpoints always had. A post-only order or replacement that would cross the book is rejected with the code POST_ONLY_WOULD_CROSS — on REST it rides the error envelope’s optional code field; on gRPC it is a google.rpc.ErrorInfo status detail with reason set to the code and domain set to matching-engine.0xmonaco.com. Match on the structured code; the "post-only order would cross" message substring still works but is now a legacy fallback. error, message, and the HTTP/gRPC statuses are unchanged, and code is omitted entirely when no engine code applies. The MCP place_limit_order and replace_order tool descriptions were updated to state this contract.

order_id filter on gRPC user-trade history

TradesService.ListUserTrades (/monaco.api.trades.TradesService/ListUserTrades, authenticated, gRPC-only) adds an optional order_id string (an order UUID). When present, the response includes only trades where that order placed the caller’s own side of the fill, across hot and archived history and in both pagination modes. ListUserTradesResponse is unchanged. Adding or changing order_id invalidates an existing page token — restart the cursor walk with an empty token.

PitPass React hooks

@0xmonaco/react adds useRewardsBalance (getRewardsBalance), useTraderCodeInfo (getTraderCodeInfo), and useTransferRewards (transferRewards) — each a thin useCallback wrapper over the matching sdk.pitpass.* method that resolves the client from the MonacoProvider context. useRewardsBalance and useTransferRewards require authentication; useTraderCodeInfo is a public referral-code lookup. See PitPass hooks.

Fixed

withdrawableCollateral now reports a number a transfer-out will accept

withdrawableCollateral (on margin account and risk bucket summaries — GET /api/v1/margin/accounts, GET /api/v1/margin/accounts/{marginAccountId}, GET /api/v1/margin/parent-margin-account), newWithdrawableCollateral (on every collateral transfer-in/transfer-out response under /api/v1/margin/), and marginAvailableCollateral (GET /api/v1/margin/available-collateral) previously reported freeCollateral — an equity figure that credits realized PnL, accrued funding, and unrealized mark-to-market gains that are not principal a transfer can move. The published number could far exceed anything a transfer-out would accept; a risk bucket holding realized PnL against roughly no allocated collateral advertised its whole balance as withdrawable while rejecting every transfer. All three now report the largest amount the exchange’s withdrawal gate will actually accept — for a risk bucket, capped by its allocated collateral (net of its own resting-order and position margin and funding, plus realized PnL, counting an unrealized loss but never an unrealized gain) and by the parent account’s own limit; for a parent margin account, capped by unearmarked deposited collateral and by freeCollateral less any unrealized gain. The field types, positions, and status codes are unchanged — only the value changed. Size transfers and withdrawals from this field; freeCollateral is unchanged and remains the equity/new-position headroom figure. Expect a smaller number than before for any account or bucket carrying realized PnL, accrued funding, or an unrealized gain. The same correction closes several related withdrawal-gate gaps, all with unchanged types and status codes: an unrealized gain no longer funds a withdrawal from a parent margin account; a bucket-scoped transfer-out is no longer wrongly refused on a fully-allocated account (the release the request itself performs is now credited); and a partial withdrawal can no longer strip a losing risk bucket a piece at a time below the loss it has taken, which had let the containment step bill the difference to the insurance fund. Refusals in these cases carry a distinct, bucket-aware message so an automated client does not read them as a shortfall and top up collateral to no effect.

WebSocket order-event field names corrected

Order events on ws.events.orders (OrderFilled, OrderPartiallyFilled, OrderCancelled, and the shared common fields) declared symbol, executionQuantity, and makerFee, but the matching engine has always sent trading_pair, filled_quantity, and fee (camelCased to tradingPair, filledQuantity, and fee). Consumers reading the old names received undefined — use tradingPair, filledQuantity, and fee. filledQuantity is the quantity filled in this event: the cumulative order fill for a taker, the current trade’s quantity for a maker, keyed by role. The RAW (integer, base-unit) precision fields the wire already carried are now typed too — quantityRaw, filledQuantityRaw, remainingQuantityRaw, and totalFilledRaw. The executedAt runtime value is now RFC3339 with a Z suffix (e.g. 2026-01-02T03:04:05Z) so new Date(executedAt) parses an unambiguous UTC instant; the TypeScript type is unchanged (string). @0xmonaco/react’s useUserOrders now reads its trading-pair fallback from data.tradingPair instead of the never-populated data.symbol.

applications/config 401 description corrected

The documented 401 response on GET /api/v1/applications/config (ApplicationsService.GetConfig) was corrected from “Unauthorized - Invalid or missing JWT” to “Authentication required”. The endpoint is session-authenticated (signed ed25519 session key, monacoSignature) and never used JWT/Bearer tokens. Description-only — no type shapes, status codes, or security requirements change.

Upgrade

The MCP server ships at 1.0.38 too; it runs via npx -y @0xmonaco/mcp-server, so restarting your assistant picks up the new version and the updated post-only tool descriptions. Nothing in this release is breaking. Four things worth acting on:
  1. Adopt the typed positions/liquidations streams. Use sdk.ws.positions / sdk.ws.liquidations for live risk state, and handle a 1013 liquidations close as a gap signal — resync from a REST snapshot.
  2. Wire reconnect handling through the SDK config. Pass ws: { onStatusChange, onResync } to new MonacoSDK({ ... }) to observe status and resync after reconnects without the low-level factory.
  3. Match on POST_ONLY_WOULD_CROSS for single orders. Read the structured code (REST envelope) / ErrorInfo.reason (gRPC) instead of the message substring, and read postOnly off order detail/list reads.
  4. Re-check withdrawal sizing. Size transfer-outs and withdrawals from the corrected withdrawableCollateral / marginAvailableCollateral, and fix any WebSocket order-event consumer reading symbol / executionQuantity / makerFee.