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
Newsdk.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, stableterminalReason 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 addmaxPositionSize, 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
createMonacoSDKaccepts an optionalpollingInterval(milliseconds) that tunes the internal viem public client’s transaction-receipt poll frequency. Must be a positive finite number, otherwisecreateMonacoSDKthrowsInvalidConfigError.createMonacoWebSocket(baseUrl, options)acceptsmaxReconnectAttempts,reconnectBaseDelayMs, andconnectionTimeoutMs, plus a newonResynccallback (see Changed).
Changed
One-way futures order semantics
Authenticated order creation and margin risk simulation now use one-way futures semantics:sideis the authoritative direction.positionSideis optional and deprecated; when supplied it must agree withside(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/stopLossonplaceLimitOrder/placeMarketOrdernow returns400— create position TP/SL withattachPositionTpSlafter 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 clientslippageToleranceBps 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) — passmaxReconnectAttempts 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).
Faster transaction-receipt polling
The SDK’s internal viem public client now polls for receipts every 750ms by default (previously viem’s 4000ms), sosdk.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
Thedevelopment 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 cumulativefees, 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 returns409 Conflict(“resource already exists”) / gRPCalready_exists, instead of a500that 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 returns400“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 with400 Bad Requestinstead of forwarding it to a lookup that returned a misleading404. Valid non-negative lookups are unchanged —404still means the index was never allocated.
Upgrade
- Move entry-attached TP/SL to
attachPositionTpSl. AnyplaceLimitOrder/placeMarketOrdercall that passestakeProfit/stopLossnow returns400. Open the position first, then attach TP/SL — optionally with the newclosePositionandocoflags. - Drop
positionSide; supplyleverage. Rely onsidefor direction, and passleverageon normal margin orders (omit it only for reduce-only orders). - 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.
slippageToleranceBpsonly tightens the 500 bps band. - Generate a fresh session keypair per session to avoid the new
409on reuse. - Reconsider WebSocket reconnect handling. Auto-reconnect now retries indefinitely by default — set
maxReconnectAttemptsfor a finite cap, and useonResyncto refetch snapshots and reconcile after a reconnect.

