Skip to main content

Monaco Protocol SDK v1.0.26

This release introduces one-way futures order semantics, faucet quota introspection, a mandatory market-order price band, and a hardened WebSocket reconnect contract. It rolls up the consumer-facing changes since v1.0.24 — v1.0.25 was a backend-only deploy — and adds new SDK surface plus observable behavior changes on every client (TypeScript, Rust REST, and Rust gRPC), so upgrading is recommended and there are a few action items worth reviewing before you deploy.

Added

Faucet quota introspection

New sdk.faucet.getInfo() (GET /api/v1/faucet/info, session-authenticated) returns the authenticated wallet’s faucet quota and recent history: remainingRequests24h, dailyLimit, an optional nextAvailableAt (ISO 8601 — when the wallet’s next faucet slot frees under the current daily limit; omitted, never null, when the rolling 24h window is empty or the limit is 0), and up to five recentRequests newest-first. Quota is wallet-scoped across apps and uses the same rolling-window accounting as mint() enforcement. Call it before request_faucet/mint() to show remaining quota instead of provoking a 429. A matching get_faucet_info MCP tool ships for agents (non-mainnet networks only).

Position TP/SL: full-close legs and OCO

sdk.positions.attachPositionTpSl (POST /api/v1/positions/{positionId}/tp-sl) adds a per-leg closePosition flag that resolves the full live position size when the leg triggers (omit quantity when closePosition: true), and a request-level oco flag that cancels the sibling leg after one triggers (oco: true requires both a take-profit and a stop-loss leg).

Order cancellation cause

Order responses and WebSocket order events now expose a nullable, stable terminalReason when an order is cancelled: USER_REQUESTED, REPLACED, LIQUIDATION, INSUFFICIENT_MARGIN, REDUCE_ONLY_EXHAUSTED, POSITION_CLOSED, OCO_SIBLING_TRIGGERED, or SYSTEM.

Perp risk tiers: base-quantity position cap

Perp risk tiers add maxPositionSize, the authoritative maximum absolute base-asset quantity for the tier. maxPositionNotional remains as a deprecated compatibility alias (it always carried a base quantity, not a notional) — read maxPositionSize.

SDK configuration and WebSocket options

  • createMonacoSDK accepts an optional pollingInterval (milliseconds) that tunes the internal viem public client’s transaction-receipt poll frequency. Must be a positive finite number, otherwise createMonacoSDK throws InvalidConfigError.
  • createMonacoWebSocket(baseUrl, options) accepts maxReconnectAttempts, reconnectBaseDelayMs, and connectionTimeoutMs, plus a new onResync callback (see Changed).

Changed

One-way futures order semantics

Authenticated order creation and margin risk simulation now use one-way futures semantics:
  • side is the authoritative direction. positionSide is optional and deprecated; when supplied it must agree with side (BUY↔LONG, SELL↔SHORT).
  • Leverage is required for normal margin orders and optional for reduce-only orders.
  • Entry-attached TP/SL is rejected. Passing takeProfit/stopLoss on placeLimitOrder/placeMarketOrder now returns 400 — create position TP/SL with attachPositionTpSl after the position opens.

Market-order price protection

Market orders now execute inside a mandatory server-side protective price band of 500 bps around a robust reference price (the mark price, or the median of best bid, best ask, and last trade). Fills beyond the band are cut off: the portion that fills inside the band settles and any remainder is cancelled; if nothing can fill inside the band the order is rejected as insufficient liquidity; and a market order on a market with no usable reference price is rejected as unavailable. A client slippageToleranceBps can only tighten the band, never widen it. The order-risk simulation endpoints reflect the same band and can return the same rejections. No wire types changed.

WebSocket reconnect contract

Auto-reconnect now retries indefinitely by default with full-jitter exponential backoff capped at 30s (previously it gave up after 5 attempts) — pass maxReconnectAttempts to restore a finite cap. onStatusChange now also reports "reconnecting" while backing off, the client answers a server-initiated {"type":"Ping"} with a Pong, and a new onResync callback fires after every automatic reconnect once the connection is re-authenticated and re-subscribed. onResync receives slowClient: true when the server shed the connection with close code 1013, so apps can refetch REST snapshots and reconcile by (id, updatedAt).
Correction since this release. The original wording is left in place above; the correction is:
  • “reconcile by (id, updatedAt)” — for orders that key cannot converge. Order detail reports updatedAt from the matching engine’s event clock while order lists report it from the database commit clock, so the two endpoints report different values for the same order, and the commit clock can move without any state change. Orders now carry an opaque, compare-only version on the REST responses, on every order WebSocket event, and on the orders snapshot frame: reconcile on (id, version), taking a snapshot row when snapshot.version >= local.version, and falling back to a full field comparison when it is absent (an order last written before the field existed). See Order state versions.

Faster transaction-receipt polling

The SDK’s internal viem public client now polls for receipts every 750ms by default (previously viem’s 4000ms), so sdk.vault.deposit and sdk.waitForTransaction resolve within about one to two Sei blocks. Set pollingInterval: 4000 to restore the previous cadence.

Faucet daily-limit counting

The faucet daily limit now counts every mint call in the rolling 24h window rather than distinct clock minutes, closing a bypass where multiple calls in the same minute consumed a single quota slot. getInfo()’s remainingRequests24h and nextAvailableAt reflect the same per-call counting; the minute truncation on recentRequests[].requestedAt is display grouping only.

Development WebSocket ingress

The development network preset now connects WebSockets to wss://ws-develop.apimonaco.xyz/ws. The previous wss://develop.apimonaco.xyz/ws path remains supported during the migration window, so older SDK versions and custom wsUrl overrides keep working unchanged.

Fixed

Batch-replace taker fees attributed to the owning account

The PnL service now attributes the taker fee on a batch-replace fill to its owning margin account. A batch replace produces multiple result orders and carries no top-level taker order, so those taker fees were previously dropped — understating persisted per-account fee totals, most visibly for market-making accounts that reprice via batch replace. This corrects the cumulative fees, realizedPnl, and totalPnl behind getPortfolioChart for data recorded after this change.

Security

  • Session public-key reuse returns 409. Re-running verify or creating a delegated-agent session with an already-registered session public key now returns 409 Conflict (“resource already exists”) / gRPC already_exists, instead of a 500 that leaked the Postgres detail and unique-constraint name. Generate a fresh session keypair per session.
  • Hardened error envelopes. Other database failures return a generic 500 “Database error” and a malformed JSON body returns 400 “Invalid JSON payload” — none echo engine detail, constraint names, query text, or the raw parser message. Successful requests are unchanged.
  • Withdrawal-index validation. Public GET /api/v1/withdrawals/{withdrawal_index} now rejects a negative or non-integer index with 400 Bad Request instead of forwarding it to a lookup that returned a misleading 404. Valid non-negative lookups are unchanged — 404 still means the index was never allocated.

Upgrade

Action items to review:
  1. Move entry-attached TP/SL to attachPositionTpSl. Any placeLimitOrder/placeMarketOrder call that passes takeProfit/stopLoss now returns 400. Open the position first, then attach TP/SL — optionally with the new closePosition and oco flags.
  2. Drop positionSide; supply leverage. Rely on side for direction, and pass leverage on normal margin orders (omit it only for reduce-only orders).
  3. Handle new market-order outcomes. A market order can now come back partially filled with the remainder cancelled, or be rejected as insufficient liquidity or market unavailable. slippageToleranceBps only tightens the 500 bps band.
  4. Generate a fresh session keypair per session to avoid the new 409 on reuse.
  5. Reconsider WebSocket reconnect handling. Auto-reconnect now retries indefinitely by default — set maxReconnectAttempts for a finite cap, and use onResync to refetch snapshots and reconcile after a reconnect.