Skip to main content

Release Notes

Stay up to date with the latest Monaco Protocol SDK releases, new features, and improvements.

Monaco Protocol SDK v1.0.67

Latest Stable Release

Added

  • Screener open-interest averages — sdk.market.getScreener() (GET /api/v1/market/screener; gRPC MarketService.GetScreener) rows gain nullable openInterestNotionalAvg1h, openInterestNotionalAvg24h and openInterestNotionalAvg7d, and ScreenerSnapshotPoint gains openInterestNotionalAvg — means of one valued gross open-interest notional per completed UTC minute, published once 95% of the window’s minutes carry a sample. Spot pairs and under-covered windows are null; no backfill
  • createdAt on conditional-order frames — sdk.ws.conditionalOrders() live frames and snapshot rows carry createdAt, matching REST; optional on ConditionalOrderEventData only for older deployments during a rolling release

Changed

  • Isolated position backing excludes open-order funds — isolated positions use filled-position backing (committed principal, realized PnL, signed fees — a maker rebate adds — and funding), reported as isolatedMargin on GET /api/v1/positions and /positions/{position_id} and used for liquidationPrice there and on /positions/{position_id}/risk, excluding funds reserved for unfilled orders; live and persisted reads agree. riskBucketCollateral funds order capacity, and only fills commit it to the position. Field names and types are unchanged
  • Conditional-order and TWAP listings gain cursor pagination — GET /api/v1/orders/conditional / GET /api/v1/orders/twap (gRPC OrdersService.ListConditionalOrders / ListTwapOrders) walk (createdAt, id) newest-first by pageToken / nextPageToken, pageSize up to 1000, with total / page returned as 0. The server uses cursor mode only when pageToken is sent (raw clients start with ""); the SDK’s listConditionalOrders / listTwapOrders default to it. The deprecated page still selects page-number mode

Fixed

  • The liquidations channel has a baseline and reports recoveries — ws.liquidations receives a subscribe-time snapshot of your UNRESOLVED records, and a recovered episode now sends a terminal alert with status: "COMPLETED" and accountState: "Normal" instead of ending silently
  • Signing survives a skewed device clock — the SDK calibrates from each response’s Date header, signs with the corrected time once the offset reaches 5 seconds, and retries once after a clock-correcting 401; a skew-rejected WebSocket handshake re-signs up to 3 times, recovering once a REST response has calibrated the clock
  • Batch-cancel cancelledAt is the engine’s stamp — millisecond precision, matching a later read of the order to the millisecond, instead of the API clock truncated to seconds
  • Funding premium samples are bounded before averaging — each sample is capped to the maximum funding rate and every sample in the window carries equal weight
  • Higher WebSocket limits — 1,024 subscriptions per connection, burst 1,200, 50 authenticated connections per account per instance; a per-connection byte budget refuses bursts of very large frames with MESSAGE_RATE_LIMIT

Monaco Protocol SDK v1.0.66

Added

  • Trailing stops — sdk.positions.attachPositionTpSl (POST /api/v1/positions/{position_id}/tp-sl; gRPC PositionsService.AttachPositionTpSl) accepts trailingStop: TrailingStopLeg (trailBps 10–2000, activationPrice?, quantity? / closePosition?, slippageToleranceBps?, expiresAt?), alone or alongside takeProfit / stopLoss, and returns trailingStopOrderId; placeLimitOrder / placeMarketOrder (POST /api/v1/orders; gRPC OrdersService.CreateOrder) accept the same leg as options.trailingStop on margin entry orders that are not reduce-only, arming only on a full fill (the entry response carries trailingStopOrderId only when the stop materializes — a full fill on arrival or a resting remainder). A reduce-only market close whose trigger trails the best mark since arming (long: watermark × (1 − trailBps/10000)) and only moves in the position’s favour. One per position — a second attach returns 409; no modify; never joins oco. ConditionalOrder and conditionalOrders frames gain conditionType: "TRAILING_STOP", trailBps, activationPrice, watermarkPrice and trailArmedAt; live frames carry reason: "armed" and reason: "ratcheted"

Changed

  • Position TP/SL attach is metered by the order-creation rate limit — each attachPositionTpSl call draws one item from the order-creation budget, whatever legs it carries; over budget it returns 429 RATE_LIMIT_EXCEEDED (gRPC RESOURCE_EXHAUSTED). Position closes stay unmetered; enforcement is warn-only until turned on for the environment
  • Position.version advances only when the position changed — an unchanged position keeps its version, so a ranked live position_update for an untouched sibling of a filled position can carry a higher version than the REST row with identical content; rank it as newer. No field or type change

Fixed

  • The WebSocket client stops reconnecting after authentication is lost — after SESSION_INVALID + 1008 or AUTH_FAILED, the client drops the rejected session, keeps handlers, reports "disconnected" and calls the new onReauthenticationRequired({ code, message, closeCode }) once; a fresh session reconnects, resubscribes and fires onResync. Other abnormal closes keep the backoff reconnect
  • onError names the channel a per-channel failure concerns — WebSocketErrorInfo gains channel?: string on INVALID_SUBSCRIPTION, AUTH_REQUIRED, SUBSCRIPTION_LIMIT and SNAPSHOT_UNAVAILABLE frames; connection-wide failures stay { code, message }

Monaco Protocol SDK v1.0.65

Changed

  • Page-number listing counts are bounded by their pagination reach — sdk.profile.getUserTrades (GET /api/v1/accounts/trades; gRPC AccountsService.GetUserTrades / TradesService.ListUserTrades), sdk.positions.listPositions / listPositionHistory, sdk.marginAccounts.getMarginAccountMovements / getParentMarginAccountMovements, and sdk.trading.listConditionalOrders / listTwapOrders now bound the legacy page-number total (and getUserTrades’s totalPages) at each listing’s pagination reach: exact up to pageSize × 10,000 matching rows and saturating there, so a caller with more history than page-number mode can page through reads the cap instead of its lifetime total. page above 10,000 is rejected (page must be <= 10000). Unlike v1.0.64’s totalCapped on order history, this is the saturating shape — no new field; the response schema is unchanged and only the count values move, for callers above the reach. Only getUserTrades has a cursor (pageToken) mode — its SDK default, unchanged and still reporting no totals; the other six are page-number-only (page / pageSize), and their total is the count they report, not deprecated

Fixed

  • The twapOrders channel delivers a subscribe-time snapshot — sdk.ws.twapOrders(handler, tradingPairId?, onSnapshot) now receives a subscribe-time snapshot ahead of the live frames: one TwapOrderEventData row per owned non-terminal (PENDING/ACTIVE) TWAP parent, optionally scoped to the subscription’s pair, each the shape of a live twap_order_update with reason: "snapshot". Previously the server sent no TWAP snapshot, so onSnapshot was inert and a baseline needed a REST read. The baseline reaches only subscriptions that pass an onSnapshot handler — a caller already passing one needs no code change, a caller subscribing as sdk.ws.twapOrders(handler) should add one; an empty baseline arrives as onSnapshot([]), and an unavailable read surfaces SNAPSHOT_UNAVAILABLE while the subscription stays active
  • The instruments channel carries base and quote asset icon URLs — sdk.ws.instruments(handler, tradingPairId?, onSnapshot) now surfaces baseIconUrl and quoteIconUrl (string | null) on each parsed InstrumentEventData, on both the snapshot and live frames. The server sends each key on every market as an explicit null when that asset has no configured icon (never omitted, never synthesized), and the parser reads it tolerantly; the two fields are optional so the addition is non-breaking. A client can join a position’s tradingPairId to the instrument row and render both icons without a REST read

Monaco Protocol SDK v1.0.64

Added

  • Order history returns a bounded item count in both pagination modes — sdk.trading.getPaginatedOrders (GET /api/v1/orders, authenticated; gRPC OrdersService.ListOrders) now populates total and totalPages in both page-number and cursor (pageToken) mode, bounded by a server ceiling of 10,000 so a large order history never triggers an unbounded COUNT; cursor mode previously returned 0. GetPaginatedOrdersResponse gains totalCapped (boolean): true when more than the ceiling matched, so total/totalPages are a lower bound. total and totalPages are no longer deprecated; only page stays 0 in cursor mode. The response schema is additive, but the count semantics change: cursor mode now populates total/totalPages (a count over full history, hot and archived merged) where it returned 0, and a page-number caller above the ceiling in its recent-history window reads the capped 10,000 instead of the exact count of that window — check totalCapped to tell an exact count from a saturated one

Monaco Protocol SDK v1.0.62

Breaking

  • The account channel snapshot makes AccountEventData.sequence optional — sdk.ws.account(handler, onSnapshot) now delivers a subscribe-time snapshot ahead of the live diffs: one AccountEventData row per margin account you own in the authenticated application, including flat accounts the live producer never emits for, each carrying its current persisted health (a flat, otherwise-empty account reports its deposited collateral as both equity and freeCollateral, with a zero maintenance requirement and margin ratio). Because a snapshot row is a live payload with sequence omitted, AccountEventData.sequence is now number | undefined — SOURCE-BREAKING for strict TypeScript: guard event.data.sequence, treat absent as unknown (never zero, and never a replay or ordering cursor), while live frames still always carry it. Merge the snapshot per marginAccountId and let any live frame supersede a snapshot row for the same account; rank updatedAt only between live frames, since a snapshot row’s updatedAt is a persist time and a live frame’s is a sample time. No SDK migration is needed, but the baseline reaches only subscriptions that pass an onSnapshot handler (add one if you don’t already)

Added

  • PitPass self-scoped referral reads — sdk.pitpass.getMyReferralPosition() (GET /api/v1/pitpass/referrals/me) and sdk.pitpass.getMyReferralDownline() (GET /api/v1/pitpass/referrals/me/downline; gRPC TraderCodeService.GetMyReferralPosition / GetMyReferralDownline) return the caller’s own referral chain — authenticated and self-scoped, with no id path parameter, so a request cannot read another user’s chain, and every other user is exposed only as a public wallet address plus a custom-handle-or-wallet display. getMyReferralPosition returns your direct (L1) referrer, all-time totalEarned, and up to your 100 most recent ReferralRewardEntry rows (totalEarned stays the complete aggregate); getMyReferralDownline returns earningsBySource across L1/L2/L3, your L1 directReferees roster (including referees who have not traded yet, showing up to the 500 most recent effective referees, drawn from a bounded scan so a very large history may yield an incomplete roster; the earningsBySource/summary earnings views stay unbounded), and an always-populated per-level summary. @0xmonaco/react wraps both as the useMyReferralPosition and useMyReferralDownline hooks
  • The verify response reports isNewUser and referralApplied — AuthState (from sdk.auth.authenticate / sdk.auth.verifySignature, POST /api/v1/auth/verify) now carries two optional flags: isNewUser is true when this verify created the user, and referralApplied is true when a PitPass referral was recorded on it. Read both behind a presence check

Changed

  • The market_stats channels deliver a subscribe-time snapshot — sdk.ws.marketStats(pairId, handler, onSnapshot) and sdk.ws.marketStatsAll(handler, onSnapshot) (public, no auth) now receive a snapshot ahead of the live frames: a one-element MarketStatsData[] for the per-market form, a MarketStatsSummary[] (one per active market with retained data, ordered by trading-pair id) for the all-markets form — the per-market row matching the live per-market payload, and each all-markets summary matching an element of the live all-markets frame’s markets list. A per-market subscription whose market has not produced a frame yet receives a SNAPSHOT_UNAVAILABLE signal rather than a fabricated row, and stays active. No SDK migration is needed, but the baseline reaches only subscriptions that pass an onSnapshot handler (add one if you don’t already)
  • Application listings bound total and totalPages to their pagination reach — client.applications.listApplicationOrders() and client.applications.listApplicationMovements() (GET /api/v1/applications/orders and /api/v1/applications/movements, server-key auth; gRPC ApplicationsService.ListAppOrders / ListAppMovements) now cap total and totalPages at the listing’s reach: both stay exact up to 10,000 pages of the requested pageSize (pageSize × 10,000 matching rows) and saturate there, so an application with more matching rows reads the cap with totalPages = 10000 instead of its lifetime total. Loops that stop at page >= totalPages keep terminating; only code reading total as a lifetime count needs to treat pageSize × 10,000 as “at least this many”, and a page above 10,000 is now rejected as invalid input

Monaco Protocol SDK v1.0.61

Breaking

  • riskTiers carries the full margin ladder — GET /api/v1/market/pairs/{trading_pair_id}/perp/config (sdk.market.getPerpMarketConfig; gRPC MarketService.GetPerpMarketConfig, public, no auth) now lists every notional bracket ascending by lowerBoundNotional, where it previously reported a single synthetic tier, so both the length and the meaning of the array change. Each RiskTier gains tierLevel (1-based, 1 is the lowest bracket), lowerBoundNotional ("0" for tier 1), maxLeverage (optional — absent only on a single-tier market configured from explicit margin rates), initialMarginDeduction and maintenanceMarginDeduction ("0" for tier 1). The deductions are the continuity terms of IM_floor(N) = N x initialMarginRatio - initialMarginDeduction and MM(N) = N x maintenanceMarginRatio - maintenanceMarginDeduction, and each bracket’s rates are marginal. A position’s bracket is the last tier whose lowerBoundNotional is at or below abs(quantity) x markPrice, and its actual initial margin is the greater of the ladder floor and the selected-leverage requirement scaled to the current mark. The same ladder now prices maintenance margin and liquidation prices on the position, portfolio and margin-account reads. Top-level maxLeverage / initialMarginRatio / maintenanceMarginRatio are unchanged and remain the tier-1 values, and maxPositionSize (with its deprecated maxPositionNotional alias) stays a market-wide base-quantity cap repeated on every tier

Added

  • Order-risk previews return the engine’s simulated fill — sdk.marginAccounts.simulateOrderRisk, simulateParentMarginOrderRisk and simulateRiskBucketOrderRisk (authenticated, owner-scoped; gRPC MarginAccountsService.SimulateOrderRisk / SimulateParentMarginOrderRisk / SimulateRiskBucketOrderRisk) now publish the match the engine already ran to admit the order. expectedMatchResult?: MatchResult is the same shape CreateOrderResponse.matchResult carries for a placement (the Order read model does not carry it), from the same walk over the live book — totalFilled, remainingQuantity (the partial-fill indicator), averageFillPrice, executionPriceRange, status, actualSlippageBps, maxSlippageBps — and referencePrice?: string is the touch a MARKET preview was measured from. Both are present only when accepted is true and absent, never zeros, on every refused preview including the post-match maker-risk rejection; referencePrice is additionally MARKET-only, and a LIMIT or IOC preview is measured against its own limit price with actualSlippageBps null when every fill improved on it. The three requests gain an optional slippageToleranceBps (MARKET only, 0–1000), validated client-side by the newly exported MarketSlippageToleranceBpsSchema, which tightens the previewed walk to the band the real order will run under; it can only tighten. The preview now runs under the caller’s wallet, so self-trade prevention classifies your own resting orders as placement does. These price against the book at preview time, so apply actualSlippageBps to a TP/SL trigger price rather than displaying the absolute average. The MCP simulate_parent_margin_order_risk and simulate_risk_bucket_order_risk tools take the same input and surface both fields

Fixed

  • A margin reduce-only replace can be sized down below its filled amount — sdk.trading.replaceOrder (PUT /api/v1/orders/{orderId}) and each item of sdk.trading.batchReplace (POST /api/v1/orders/batch-replace; gRPC OrdersService.ReplaceOrder / BatchReplaceOrders) rejected quantity at or below the order’s filledQuantity on every order, with New quantity N must be greater than filled quantity 3. That rule belongs to total semantics — a new total at or below what already filled leaves nothing to rest — and a margin reduce-only quantity is not a total: it is a close size measured against the live position and placed exactly as given. Applying the rule to it made a partially filled close unshrinkable, so on a reduce-only close of 8 with 3 filled both quantity: "2" and quantity: "3" were refused. Both now succeed and are placed at the requested close size — as with any replacement, success does not mean the order is resting, since matching and self-trade prevention can fill it immediately or cancel it under CANCEL_TAKER / CANCEL_BOTH or as a SKIP remainder, so read the new order’s status — and the replacement still inherits the original’s fill history. Nothing else changes: total semantics, the total - filled subtraction and the must-exceed-filled rejection are unchanged for every other order, and the reduce-only position bound still rejects a close larger than the live position. No request or response shape changed, so clients on any SDK version receive it

Monaco Protocol SDK v1.0.60

Breaking

  • A cross risk-bucket preview no longer takes selectedTradingPairIds — sdk.marginAccounts.simulateRiskBucketOrderRisk (POST /api/v1/margin/risk-buckets/simulate-order-risk) drops the field for marginMode: "CROSS"; passing it throws ValidationError naming request.selectedTradingPairIds before any request is made, and the type is now ?: never, exactly as strategyKey already was. Cross scope is derived from trading and the preview never priced anything from the list — the engine prices the positions it holds whatever the caller declared — so its three rules only ever produced 400s. A cross preview with no list, one omitting tradingPairId, and one omitting a pair with an open cross position now all succeed, with unchanged figures. The wire field survives as deprecated, accepted and ignored, so raw REST and gRPC callers keep working; it is still rejected for ISOLATED. The response field of the same name now reports the derived scope the preview ran against — the cross bucket’s recorded pairs, every pair with an open position in it, and the previewed pair — never an echo of what you sent. The MCP simulate_risk_bucket_order_risk tool drops the field and its three refinements, declares positionSide, and is now strict: an unknown key is refused with the key named

Added

  • A take-profit / stop-loss and its close are linked in both directions — ConditionalOrder gains triggeredOrder?: TriggeredOrderSummary (orderId, status, filledQuantity, optional averageFillPrice, totalTakerFees, filledAt, terminalReason), read from the triggered order’s own row at request time on getConditionalOrder, listConditionalOrders and under Order.conditional on getPaginatedOrders rows. Present only once state is TRIGGERED and the close can be read; absent on a FAILED conditional, which recorded no triggeredOrderId to join on — a close the engine placed and the book then rejected still persists as an order row carrying conditionalOrderId, while one refused before placement produces no order row at all — and absent on the conditional_orders WebSocket frames. status is copied verbatim from the close’s row, so switch on the full OrderStatus union with a default branch: FILLED is observable in the window before a filled leg reaches SETTLED, and CANCELLED covers a MARKET/IOC leg whose remainder was cut after a partial fill, so it can carry a nonzero filledQuantity. Order.conditionalOrderId is the reverse pointer on getOrder and getPaginatedOrders, REST-only — no order event or snapshot row carries it — and absent on orders you placed, on TWAP children, and on closes older than the field, with no backfill. @0xmonaco/react’s useUserOrders carries it through as a REST-only field. Compare triggeredOrder.averageFillPrice with triggerPrice as exact decimals rather than with === on the raw strings, and note the summary is a repository read while getOrder is cache-first, so persistor lag can leave the two showing different states rather than merely different scales. Trigger price and fill price are independent — the mark is a reference price sampled on a tick, not the book’s midpoint, so there is no guaranteed minimum gap in either direction
  • Managed maker assignments — sdk.managedMarkets is a new authenticated namespace with listAssignments, claimLease and refreshQuotes, matched by GET /api/v1/managed-markets/assignments, POST /api/v1/managed-markets/{launchId}/lease and POST /api/v1/managed-markets/{launchId}/quotes, and by the gRPC ManagedMarketsService. It drives the maker side of an operator-managed launch: a 30-second lease fences one process at a time and the engine restates a quote set whose prices, sizes, fees and risk bucket are all server-derived. All three need a wallet session and refuse delegated-agent sessions with 403, but only the launch-scoped claimLease and refreshQuotes require the session to match the configured maker user and application; listAssignments filters by the caller and returns an empty page to a non-maker. ownerId is a process fence, not authorization. generation and quoteRevision are uint64 fences carried as exact decimal strings in JSON — never round-trip them through Number — and an unknown quote outcome is reconciled through claimLease before advancing the revision

Changed

  • A TRIGGERED conditional row mirrors its close — in getPaginatedOrders (GET /api/v1/orders), a CONDITIONAL row whose conditional has triggered now reports the order it fired into instead of a synthesized FILLED with filledQuantity: "0": quantity is the resolved leg size, filledQuantity / averageFillPrice / quoteVolume and the terminal instants are the close’s own, and status is the close’s current status. The status filter applies the same rule. Aggregation needs two rules: the fee fields stay absent on that row (the close’s own row carries them, and conditional.triggeredOrder.totalTakerFees repeats the total), so fees sum cleanly over a page — but the close is listed again as its own MARKET or LIMIT row, so sum quantities over those rows only
  • A triggered leg can be cancelled but not replaced — sdk.trading.replaceOrder (PUT /api/v1/orders/{orderId}) and sdk.trading.batchReplace refuse to replace the LIMIT leg a TP/SL fired into — an order carrying conditionalOrderId — the way a TWAP slice child is refused: a replacement would rest under a new id the conditional does not point at, severing the link both ways. The refusal is an INVALID_ORDER rejection naming the conditional order and the original leg stays resting. Cancelling a triggered leg is unchanged
  • Public market data hides an operator-managed launch until it is live — GET /api/v1/market/pairs, the pair detail reads, GET /api/v1/market/screener and the public price reads exclude a managed market until its durable runtime projection reaches live, including when isActive=false is passed explicitly. A pair with no launch row keeps its existing visibility

Monaco Protocol SDK v1.0.59

Breaking

  • BridgeRoute gains dstEid, RouteKind gains Oft — @0xmonaco/contracts: the SWEEPER_FACTORY_ABI tuple takes a uint32 dstEid between compose and bridgeToken, so calldata encoded or decoded against the previous shape mismatches. 0 uses the chain-wide hub EID; a non-hub value builds a multi-hop lane. RouteKind gains Oft (4) for vanilla LayerZero OFT/OFTAdapter lanes, and SWEEPER_ABI gains OftCannotUnwrap — an Oft leg with unwrapNative is refused. Nothing is deployed on-chain with the previous shape

Added

  • The BuilderCodes API — sdk.buildercodes gains five authenticated methods, matched by REST endpoints and the gRPC BuildercodeRewardsService: getConfig(), listPayouts({ page?, pageSize? }), getPayoutSummary(), getRewardsBalance(), and claimBuildercodeRewards({ token, amount }), which moves accrued revenue share into a tradeable balance (ledger-only, no on-chain transaction). All five are caller-scoped — the payout bucket resolves from the caller’s own wallet, never from the request — and refused for delegated-agent sessions. The three application reads require the application’s current payout wallet, while getRewardsBalance reads the authenticated wallet’s own bucket, so a former payout wallet can still claim what it accrued before a rotation. An ambiguous claim failure is an unknown outcome, not a rejection, and that covers every status at or above 500 plus a failure with no status at all — not only 503: the command is admitted to the sequencer before its reply is sent and there is no idempotency key, so the SDK clears retryable on all of them, a balance read does not settle it, and only the tagged OPERATIONS_BLOCKED and OVERLOADED cases are safe to retry
  • Resting-order caps — the matching engine bounds resting orders at 500 per market and 4,000 per account by default (per engine shard), both tunable per deployment through RESTING_ORDER_CAP_PER_MARKET / RESTING_ORDER_CAP_PER_ACCOUNT — development already runs 8,000 per account — rejecting the excess with 400 and the structured code ORDER_CAP_EXCEEDED, whose message names the binding scope. A book-state guard rather than a rate limit: fills and cancels free slots immediately, MARKET and IOC/FOK orders never count, replaces are exempt so repricing at the cap always works, and batch-cancel-all is exempt from these caps, keeping its own separate 20,000 matching-active-order ceiling unchanged. Conditional (TP/SL) and TWAP orders keep their own caps and count only once triggered onto the book
  • A durable version on the position reads — the REST list and detail reads, the gRPC Position message, the WebSocket positions snapshot and ranked live position_update frames all carry an opaque producer-owned row revision; reconcile on positionId + version. The risk read is the exception: GetPositionRiskResponse carries no version, so rank a position from a list, detail or WebSocket read instead. The merge rule is per field group: on an equal version skip the versioned fields as an idempotent redelivery but still take markPrice, unrealizedPnl, liquidationPrice, leverage, maintenanceMarginRequired and initialMarginRequired from the newer payload, since those six are re-derived on every read and carry no version. isolatedMargin is exempt too, despite being versioned: the snapshot overlays it from an independent live engine read, so take it from the newer payload as well. 0 is the unranked sentinel — treat it exactly as absent, and note a live-served REST or gRPC read reports 0 where an unranked WebSocket frame omits the field
  • Permissionless sweeps take a LayerZero fee — @0xmonaco/contracts: the parameterless SWEEPER_ABI sweep(address) and the factory’s permissionless sweep(applicationId, user, token) are now payable, with excess refunded to the caller, so a fresh deposit address can deploy and sweep in one call. SWEEPER_ABI gains sweepWithRefund(token, feeRefundAddress), and direct Stargate routes join the permissionless path behind the owner-set permissionlessBridgeFeeCapBps (default 0, so the path is opt-in per chain), with new errors on two different contracts — BridgeFeeTooHigh and UnexpectedBridgeDebit on SWEEPER_ABI, InvalidFeeCap on SWEEPER_FACTORY_ABI — so decode a revert against the matching ABI

Changed

  • Contract-wallet (EIP-1271) login for BuilderCodes payout wallets — sdk.auth.signChallenge asks the wallet to authorize the message rather than sign it with a private key: an EOA uses EIP-191 personal_sign, a smart-contract wallet such as a Safe returns a signature its own contract accepts. Submit the returned value unchanged and do not assume the 65-byte EOA shape. This is not general smart-contract-wallet login — it is offered only to a wallet holding a BuilderCodes payout bucket, on a deployment with the flag on and a chain RPC configured. Three consequences: POST /api/v1/auth/verify (gRPC Verify) can answer a retryable 503 / UNAVAILABLE that is not a verdict on the signature (a rejected signature, including a contract that reverts, is still 401); signature publishes a bound of 8192 decoded bytes (16386 characters with 0x), above which it is rejected rather than forwarded to the chain; and the challenge lifetime is 30 minutes rather than 5 for every address while that login path is enabled, uniform so the unauthenticated endpoint cannot be used to probe which addresses hold a payout bucket
  • The positions channel carries scope and margin requirements — sdk.ws.positions(...) now surfaces riskBucketId, marginMode, maintenanceMarginRequired and initialMarginRequired on both snapshot rows and live position_update frames, so a handler no longer needs a REST read to learn a position’s risk bucket or requirement at the mark. All four are read leniently; do not infer either scope field from the other, and treat a missing marginMode as unknown rather than defaulting it to CROSS. updatedAt now reports when the position was last mutated rather than when the frame was serialized, so two frames for an unchanged position repeat one timestamp and an oracle reprice no longer moves it — version remains the reconciliation key. A live-served Position.marginMode is answered by the matching engine’s own view of the bucket, so a REST read and the frame for the same position cannot disagree
  • Rate limits: family tiers everywhere, and a budget on authentication — the movement budget and the per-account read budget each gain a family tier at 4× the account caps by default (set per deployment via MOVEMENT_RATE_LIMIT_FAMILY_MULTIPLIER / READ_RATE_LIMIT_FAMILY_MULTIPLIER), shared by a master account and all of its sub-accounts, so N sub-accounts no longer mint N full budgets. POST /api/v1/auth/challenge and /verify (gRPC AuthService/Challenge and Verify) are metered per client address at 60 requests/min sustained, burst 120 by default (overridable via AUTH_RATE_LIMIT_PER_MINUTE / AUTH_RATE_LIMIT_BURST), shared by both endpoints on public ingress; the check runs before the signature, so a throttled request is never an authentication failure — REST 429 carries Retry-After and details.retryAfter, gRPC RESOURCE_EXHAUSTED carries a google.rpc.RetryInfo detail, and the hint is transport-specific — though it ships warn-only, admitting over-budget requests without a 429 until auth_rate_limit_enforced is turned on per environment

Fixed

  • payoutAddress accepts a null wire shape — GetBuildercodesConfigResponseSchema previously allowed only a string or an absent key, so a nullable payload would throw. This is wire-contract hardening, not a fix for an observed response: a successful getConfig() in the released endpoint always carries an address, since it authorizes the caller as the payout wallet and returns 403 when none is set. The inferred type widens to string | null | undefined
  • buildercodesBudgetBps and appliedBps are different numbers — buildercodesBudgetBps on getConfig() is the configured target, computed from the gross non-negative protocol take and capped by the post-floor remainder; a payout row’s appliedBps is the realized whole-bps rate after that clamp and may be lower. Field types and wire values are unchanged

Monaco Protocol SDK v1.0.58

Changed

  • Self-trade prevention dispatches by mode — an order meeting one of your own resting orders is resolved under the taker’s effective mode. Previously the engine silently skipped your resting orders and filled around them, rejecting the whole order only when the remaining third-party depth could not cover it; now the mode decides both halves. The platform default CANCEL_MAKER cancels your resting order in full and lets the incoming one carry on into the book — unless the incoming order is a zero-fill MARKET order whose only in-band depth was your own, which is rejected for insufficient liquidity before sequencing, discarding the simulated cancellation and leaving your resting order in place; CANCEL_TAKER cancels the incoming remainder at the first self-touch and leaves the resting order; CANCEL_BOTH cancels both; SKIP trades around your order, but cancels a GTC remainder rather than resting it into a surviving own order. The whole-order SELF_TRADE_NOT_ALLOWED rejection is removed from the match path, surviving only for FOK + CANCEL_BOTH. “Your own” widens from one account id to your whole wallet family — every account sharing a wallet address, across applications. Resolution is per-order mode, then wallet default, then platform default
  • A self-trade-prevention cancellation is CANCELLED, not REJECTED — it persists as a real cancelled order and emits OrderCancelled even with zero fills, unlike an accepted zero-fill IOC/FOK, which stays REJECTED — for a FOK that means insufficient qualifying liquidity, not an empty book: partial candidate depth is discarded when it cannot fill the whole quantity. (A plain zero-fill MARKET submit is refused before acceptance and emits nothing at all.) Status alone distinguishes “my own prevention ended this” from “the book had nothing”, with no message parsing
  • Cancellation events name the counterparty — OrderCancelled gains optional stpCounterpartyOrderId and OrderTerminalReason gains SELF_TRADE_PREVENTION; the two always travel together, so presence of the key is the whole signal. It names the arriving order on a swept resting order, and the first own resting order on a cancelled taker remainder. Declared on OrderPartiallyCancelled too, but nothing emits it there — self-trade prevention cancels a resting order in full
  • A swept resting spot order reports its lock release — the swept spot order’s wallet now receives the movements-channel user_movement frame (CREDIT / OrderCancelled, with the atomic locked before/after snapshot) that a user-initiated cancel produces. Previously the lock was released with only the order event emitted, so a client tracking locked spot balances off that channel drifted. A swept margin maker emits no movement frame — its reserve release moves no user_balances leg, exactly as a user cancel of a margin order emits none

Added

  • Per-order selfTradePreventionMode — optional on placeLimitOrder, placeMarketOrder, replaceOrder, batchCreate and batchReplace (and the useTrade equivalents), on POST /api/v1/orders, PUT /api/v1/orders/{orderId} and both batch endpoints, and on the gRPC OrdersService requests. Overrides the wallet and platform defaults for that order; a replacement carries its own value and inherits nothing. The requested value is echoed back on order reads and order WebSocket payloads, absent when none was sent — it is not the effective mode. A raw REST/gRPC batch item with an unknown value fails alone with INVALID_SELF_TRADE_PREVENTION_MODE; the TypeScript SDK instead validates the whole array against its batch schema and throws ValidationError before sending, so no item executes. Also on the MCP place_limit_order / replace_order tools
  • Wallet self-trade-prevention default — GET / PUT /api/v1/accounts/self-trade-prevention (sdk.profile.getSelfTradePreventionDefault / setSelfTradePreventionDefault; gRPC AccountsService.GetSelfTradePreventionDefault / SetSelfTradePreventionDefault) read and set one default per wallet family, returning { mode, isCustom } where isCustom is false while the platform default CANCEL_MAKER is inherited. The write is sequenced through the matching engine and acknowledged after the durable append, so the wallet’s next order already resolves it; the read serves an asynchronous projection and can briefly lag a just-acknowledged set. Unknown modes are 400, and the setter returns 503 when the sequenced write fails transiently — an unknown outcome rather than proof nothing was written, so re-read or retry the same mode, which is safe because the call sets an absolute value; delegated-agent sessions can neither read nor set it — both operations return 403 / PermissionDenied

Monaco Protocol SDK v1.0.57

Breaking

  • SweepParams binds a sweep to an exact amount — SWEEPER_ABI and SWEEPER_FACTORY_ABI change shape: the tuple gains a leading amount, the exact balance the quote was computed for. The sweep moves exactly that much and leaves any post-quote deposit for the next, re-quoted sweep; a balance that fell below it reverts with the new StaleQuote. amount: 0n binds nothing and is accepted only on the direct CCTP route, whose burn is exact-amount — every other route rejects it with the new QuoteNotBound. Calldata encoded against the previous tuple is rejected

Changed

  • One order listing for every kind of order — GET /api/v1/orders (gRPC ListOrders; sdk.trading.getPaginatedOrders / iterateOrders) returns book orders, TWAP parents and conditional orders in one timestamp-sorted stream, so status=SUBMITTED,PARTIALLY_FILLED is the working set across all three kinds in one call. orderType gains two response-only values, TWAP and CONDITIONAL, and the matching row carries its full detail under the new twap / conditional fields with the shared fields populated from it. In @0xmonaco/types, Order.orderType widens from OrderType to ListedOrderType — an exhaustive switch must handle the two new values. Creation still takes LIMIT or MARKET; the WebSocket orders channel still carries book orders only
  • Batch replace fails per item, not per batch — every pre-sequencing validation failure now fails its own item with a structured error.code (INVALID_ORDER_ID, INVALID_PRICE, INVALID_QUANTITY, INVALID_CLIENT_ORDER_ID, INVALID_POST_ONLY; FORBIDDEN for a delegated item without a persisted original) while the valid items proceed and the failed item’s original is untouched, matching batch create. A 400 still means the batch did not execute, but a malformed item no longer produces one — so read results[] per item, because a 2xx can carry per-item failures. Empty and oversize batches are the only input validation that still rejects the whole request. totalRequested === totalSucceeded + totalFailed now holds on both batch endpoints
  • Authenticated reads draw a per-account budget — every signed GET (plus the POST order-risk previews) and every Get* / List* / Simulate* RPC draws one request from a single per-account read budget, 30 requests/s sustained with a burst of 100 by default. Delegated sessions share the owner’s budget, sub-accounts have their own, reads return no budget headers, writes and public market data are unaffected. Over-budget reads return 429 RATE_LIMIT_EXCEEDED with Retry-After (gRPC RESOURCE_EXHAUSTED with RetryInfo) once enforcement is enabled per environment; until then they are admitted and only measured

Fixed

  • A TWAP parent’s resting slice child can no longer be cancelled directly — cancelling it used to succeed while the parent stayed live and swept that slot as a taker at its deadline, charging taker fees for an order the caller believed cancelled. CancelOrder and the batch cancel forms now refuse it and name the parent to cancel instead; CancelTwapOrder remains the way to stop a TWAP and takes the resting child down in the same step. A cancel-all sweep reports the child as one refused item and still cancels everything else — it does not stop TWAP parents
  • Unused execution collateral returns after a first-use isolated market order executes — where refunds are enabled, settlement returns the unused automatic allocation to the parent margin account while retaining position margin, fees, funding, losses, and other live order reserves. Any positive fill triggers it, a partially filled market order included, capped at the allocation the order originally drew; POST /api/v1/margin/risk-buckets/simulate-order-risk reflects the same release. Request fields are unchanged and the rollout defaults to off

Monaco Protocol SDK v1.0.56

Added

  • GET /api/v1/sweeper/chains — a public, unauthenticated listing of the chains and assets the sweeper watches: per chain its EVM chainId, short name, a hub flag for the settlement chain, and the swept assets with symbol, tokenAddress, decimals, and minSweepRaw (raw base units, as a decimal string). sdk.sweeper.getChains() in TypeScript, SweeperService.GetChains over gRPC. Registration is unchanged — the deposit address is identical on, and watched on, every listed chain
  • Durable version on spot balance rows — version appears on the spot balance REST reads, the gRPC AccountBalance, the subscribe-time balances snapshot, and live balance_update events, and carries the same value on each for identical state. Apply a higher version, treat equal as an idempotent duplicate, ignore lower; 0 and an absent field both mean unranked. Margin collateral is deliberately unversioned and must be handled fail-open. useUserBalances in @0xmonaco/react does the ranking for you
  • MESSAGE_RATE_LIMIT WebSocket error code — WebSocketErrorCode gains it; existing codes are unchanged

Changed

  • Order rate limits tightened — reduceOnly orders and batch items now draw the order-creation budget instead of the 2× risk-reduction class, which is cancels only; mass exit (POST /api/v1/orders/batch-cancel-all and its by-pair form; gRPC BatchCancelAll / BatchCancelAllByPair) and TWAP creation (POST /api/v1/orders/twap; gRPC CreateTwapOrder) are metered at one item per call and declare 429 / RESOURCE_EXHAUSTED; and sub-accounts share a family budget with their master account at 4× the per-account caps, on top of their own. x-ratelimit-remaining-second / -minute report the tighter tier. Enforcement still rolls out per environment via the existing flag
  • Inbound WebSocket messages capped per connection — 20 per second sustained with a burst of 300 by default (operator-configurable). The first frame past the budget gets an Error frame with MESSAGE_RATE_LIMIT, then the connection closes with 1008. Protocol Ping counts; Pong, Close, and continuations do not
  • Perp maker trade legs report an empty spot balance snapshot — those legs never touch a spot balance row, so their movement’s balance snapshot fields render as 0 instead of values walked from a row the fill never moved, which could come out negative. The response shape is unchanged

Fixed

  • A margin TWAP parent provisions its risk bucket on first use — creating one for a pair with no bucket returned 400 / INVALID_ARGUMENT even though a plain margin order provisions the bucket at admission. CROSS now provisions the cross bucket allocation-neutral; ISOLATED seeds the deterministic bucket from the margin account for the first child only and tops it up as later slices reserve, releasing the seed if the engine refuses the parent. riskBucketId names the bucket. reduceOnly parents still require an existing bucket

Monaco Protocol SDK v1.0.55

Added

  • Opt-in RFC 9421 request signing — REST session authentication accepts RFC 9421 HTTP message signatures (Signature-Input, Signature, Content-Digest) over the same Ed25519 session keys, alongside the legacy X-Monaco-* headers, which stay supported with no retirement date. Monaco verifies a bounded profile: label monaco, the four covered components @method, @path, @query, content-digest in that order, created in Unix seconds within 30 seconds of server time, keyid as the 64-hex session public key, and RFC 9530 SHA-256 Content-Digest over the exact body. Presence of Signature or Signature-Input selects RFC verification with no fallback to legacy, and pairing either with an X-Monaco-* header is rejected 401. Content-Digest alone selects nothing — it is ordinary integrity metadata and may accompany a legacy-signed request. The 30-second window is not replay prevention — use endpoint idempotency
  • SDKConfig.requestSigning — createMonacoSDK({ requestSigning: "rfc9421" }) opts the TypeScript SDK in; the default stays "legacy". In RFC mode the SDK requires a string body, refuses redirects on authenticated requests, and rejects caller-supplied signature headers. RequestSigning is exported from @0xmonaco/types
  • Client::new_authenticated in the Rust REST SDK — signs the session-authenticated operations the OpenAPI contract classifies and leaves public and backend-key operations unsigned, with redirects disabled. Native targets only, since redirect policy is not enforceable on wasm32. Existing unsigned constructors are unchanged, RequestSigning::Legacy selects the legacy headers, and SessionSigner::sign_at signs a reusable reqwest request

Fixed

  • Authentication declared on 24 previously undeclared endpoints — the specification gains a monacoHttpSignature scheme next to monacoSignature, and 24 operations that declared no security at all now declare both: seven under /api/v1/positions, all fourteen under /api/v1/margin, and the three conditional order endpoints. The gateway always required a session signature on them, so this corrects the published contract rather than the enforced one — but regenerate any client built from the specification, which was not signing them. A test now asserts the specification’s declared authentication against the gateway’s route table
  • Request-body buffering scoped to signed routes — the gateway no longer buffers and hashes every request body globally. Signed routes still read and hash theirs under the same 2 MiB bound; public and backend-key routes use their normal extractor limits. No request or response shape changes

Monaco Protocol SDK v1.0.54

Breaking

  • totalTradeCountLtd retyped from string to integer — corrected to integer / int64 for market metadata, screener, and market stats. The wire value was always a JSON number; the spec wrongly declared a string. Source-level break for typed callers: @0xmonaco/types now declares number | null instead of string | null and the generated Rust SDK an integer instead of a String, so update string-specific uses (parseInt, String methods, string-typed assignment); generic formatting such as String(...) or interpolation still compiles

Changed

  • Keyed submission capacity and availability codes — ORDER_SUBMISSION_CAPACITY_EXCEEDED refuses a new idempotencyKey while retained receipts are at capacity; ORDER_SUBMISSION_UNAVAILABLE reports either a retained key whose original response cannot currently be retrieved, or saturated keyed admission — which can precede any lookup, so it does not imply the key was previously accepted. Both are retryable HTTP 503 / gRPC UNAVAILABLE, or per-item batch errors, and neither authorizes duplicate execution. A capacity refusal applied nothing and clears as protected receipts persist; an unavailable response is unknown, not a rejection. The response cache is now bounded and evictable, so a full cache alone no longer refuses new keyed submissions. The 24-hour boundary and IDEMPOTENCY_KEY_CONFLICT are unchanged
  • Per-account order rate limit declared — order endpoints document 429 / RATE_LIMIT_EXCEEDED with Retry-After and details.retryAfter, under two item-weighted classes: order creation, and risk reduction at 2x. Each enforces a per-second and a per-minute window, an over-budget batch is rejected whole, and mass-exit cancels are never limited. Withdrawals and margin collateral transfers share a separate per-user movement budget. Successful gated order responses carry X-RateLimit-Limit-Second/-Minute and X-RateLimit-Remaining-Second/-Minute. Enforcement rolls out per environment; until then over-budget requests are admitted and only measured
  • tokenSymbol on margin-collateral balance frames — balance_update reports tokenSymbol for reasons trade, fee, rebate, funding, liquidation, and adl, which could previously arrive null after a matching-engine restart. Still typed string | null; null now means only an unbound collateral token or a token that is not a configured asset
  • postOnly on maker fills — OrderFilled and OrderPartiallyFilled with role: "maker" carry postOnly: true for a post-only resting order, the last order-event variant to gain the flag. Same absent-rather-than-false contract, so data.postOnly ?? false reads correctly; additive, no type shapes change

Fixed

  • Own resting quotes no longer hide third-party liquidity — the early self-trade check counts only non-self liquidity, so a taker reaches other users’ depth resting behind your own quote at the same or a deeper price instead of being rejected. Own quotes are untouched, and the sequenced engine check still rejects with SELF_TRADE_NOT_ALLOWED before any fills when eligible third-party depth cannot cover the request
  • Every WebSocket gap closes with the resync signal — a detected broadcast gap closes the whole connection with 1013 on every subscription mix, including positions, balances, account, instruments, orderbook, ohlcv, and market-stat sockets. No new error protocol and no automatic history replay; applications still own REST reconciliation
  • Master withdrawals commit the authenticated owner — new withdrawals commit the authenticated wallet as the on-chain owner instead of a zero owner, making owner-authorized redirection possible for a future payout whose destination rejects it. Not retroactive; historical withdrawals keep their original commitments
  • Collateral transfers emit a balance update on a partial parse — a successful margin account collateral transfer emits balance_update when either engine balance component parses, overlaying the valid component and retaining the other. No event when neither parses, or when a risk bucket allocation moves no wallet balance
  • Portfolio analytics read from a dedicated reader — portfolio summary, performance, realized PnL, assets, and the sdk.profile stats and chart methods may reflect replica lag, and return 503 / Unavailable on a reader outage rather than falling back to the writer. Portfolio margin withdrawal bounds and trade history keep their freshness requirements

Monaco Protocol SDK v1.0.53

Breaking

  • Sweeper derivation uses the factory forwarder — predictSweeperAddress and sweeperCloneInitCode take forwarder, not implementation; SweeperAddressInput.implementation is renamed. Every address changes. Fresh registrations persist the forwarder-derived address, but a pre-v1.0.53 row needs an operator migration: re-registering returns and monitors the new address only in the running resolver, while restart warmup reloads its old stored address. Pause deposits for an existing registration until Monaco confirms that migration; re-registration alone is not restart-durable
  • Cross funding no longer declares pair scope — remove selectedTradingPairIds from transferCollateralToRiskBucket({ marginMode: "CROSS" }) and the MCP tool. Cross membership is derived from successful trading and remains readable on cross risk-bucket summaries. The raw REST/gRPC field is retained as deprecated compatibility input and ignored; cross risk-bucket simulation still requires its list
  • estimatedFee estimates the complete order — the three order-risk simulations price the immediately crossing quantity as taker and the resting quantity as maker. The string is signed, so negative means rebate; absence means unpriced, never free. The unchanged optional type will not flag this migration at compile time

Added

  • Order-create idempotency keys — single create and each batch-create item accept optional idempotencyKey; the same accepted payload and key return the original result for 24 hours without executing again. The TypeScript SDK generates keys when omitted, but recovery across calls or restarts requires retaining an explicit key. clientOrderId remains a separate correlation label
  • Typed WebSocket baselines — stateful sdk.ws subscriptions take an optional trailing onSnapshot callback and deliver an available subscribe-time baseline as one typed array, separate from live events. useInstruments, useUserBalances, and useUserOrders use it for reconnect repair; @0xmonaco/types adds SnapshotHandler<T> and OrderSnapshotItem
  • Portfolio risk-bucket scope — PortfolioRiskBucket adds required marginMode and isolated-only tradingPairId, so a portfolio margin response can identify each row without a second margin-accounts lookup

Changed

  • Matching-engine deadlines and resource ceilings — internal control-path RPCs now default to 30-second ordinary and 600-second batch deadlines, with per-connection concurrency/stream limits. Public adapters surface an expired internal call as REST 503 / gRPC Unavailable; a mutation can still commit after timeout, so reconcile before retrying and reuse the same order-create idempotencyKey. Keyed batchCreate instead returns overall REST 200 / gRPC OK with SERVICE_UNAVAILABLE on the expired item’s results[].error.code; the TypeScript SDK keys every item, so inspect each result and retain its original key before retrying

Fixed

  • Bounded collateral repair during execution — bucket-scoped maker fills can draw parent capacity up to the position’s initial-minus-maintenance requirement; taker remainders fund only order-caused deterioration and also respect the remaining admission budget. The tightest capacity/liquidation cap wins instead of silently removing or refusing an otherwise repairable order
  • Triggered-close event parity — TP/SL closes publish the ordinary orderbook, order, trade, OHLCV, position, and balance updates after their conditional trigger
  • First-use isolated simulation — risk-bucket order simulation models the isolated bucket a first order would create instead of failing because the bucket does not yet exist
  • Current isolated-position backing — isolatedMargin on open isolated REST/gRPC reads, position snapshots, and live updates reflects allocated bucket collateral after settled PnL, funding, and open-order reserves rather than stale posted margin

Monaco Protocol SDK v1.0.51

Breaking

  • Order fill fees use decimal quote units — fee on OrderFilled, OrderPartiallyFilled, and OrderMatched is now the normalized decimal quote-asset amount; stop dividing it by 10^quoteDecimals. New feeRaw carries the integer raw value. Negative means maker rebate; the fee remains the Monaco component only, excluding application taker fees, and aggregates the trades covered by the event
  • Order-event shapes match the wire — remove references to phantom takerOrderId / makerOrderId and fill-event totalFilled; use orderId / tradeId, cumulative filledQuantity, and incremental lastFillQuantity. OrderMatched distinguishes required execution fields, present-null trade/price/slippage fields, and optional last-fill fields; OrderExpired fill, remaining, and average-price fields are required nullable. Update null checks and fixtures
  • WebSocket value fields widen beyond known aliases — unfamiliar server values are delivered as KnownAlias | (string & {}) across order, conditional-order, position, market-stats, TWAP, instrument, OHLCV, orderbook, trade, and balance payloads. Add defaults or narrowing before assigning to closed types. Named order variants keep their closed eventType discriminants

Added

  • WebSocket onError callback — connection-scoped Error frames now reach onError({ code, message }), with exported error types and unknown-code passthrough. AUTH_REQUIRED needs authentication, release of the last local channel handler, and resubscription; SESSION_INVALID needs re-authentication; SNAPSHOT_UNAVAILABLE needs a REST baseline while live diffs continue
  • Order reconciliation version — order detail/list reads, every order event, and subscription snapshots share an optional opaque same-order comparator. Merge REST when snapshot.version >= local.version: REST wins ties because several events can share a step. Gaps are normal, absent means unknown, and cross-order comparisons are invalid. Recover gap fills through sdk.profile.getUserTrades and missing locally resting orders through targeted sdk.trading.getOrder reads
  • Conditional-order snapshot version — REST and subscription snapshots carry the comparator; live conditional events deliberately omit it. Use >= for versioned snapshots of the same conditional order and re-read affected rows to cover the database-lag gap
  • Post-only and raw order-event fields — carrying variants expose postOnly, and useUserOrders preserves it as order-level state; maker fills omit it, so absence there is not false. Rejection and expiry raw quantities are now declared, alongside the missing OrderMatched execution fields

Changed

  • Fixed UTC microseconds on selected order timestamps — ordinary-order createdAt / updatedAt / expirationDate, every conditional-order timestamp, persisted TWAP timestamps, and application order-view createdAt / updatedAt use six fractional digits and Z. Ordinary-order lifecycle stamps retain variable fractional width, and the formatting change does not make order detail/list updatedAt clocks comparable
  • batchCancelAll caps requests at 20,000 active orders — over the cap, cancels nothing and returns REST 400 / gRPC InvalidArgument

Fixed

  • WebSocket parser preservation — movements now deliver symbol and the four raw before/after balance and locked fields; unknown TWAP states and instrument changes retain their frames, and unfamiliar OHLCV price types are passed through
  • React order repair — useUserOrders narrows unknown statuses with event-implied or previous-state fallback, declines impossible placement model fields, and coalesces REST repair. Refresh/repair merges preserve live updates; the released hook still uses updatedAt, not version
  • Partial portfolio withdrawal valuation — unpriceable external withdrawals are excluded from withdrawal and dependent deposit totals with valuationStatus: "partial" on a successful response. Recognized quote assets remain unit-priced; reconciliation errors can still fail the request
  • Stable subscription paging — keyset paging prevents duplicate or skipped order, conditional-order, and position snapshot rows when concurrent updates move page boundaries
  • Liquidation maker-close cleanup — closing a position via a liquidation maker fill cancels remaining reduce-only orders as POSITION_CLOSED, releases reserves, and preserves history/PnL enrichment
  • Resilient fee refresh — bounded timeouts, a repeatable-read snapshot, last-known-good retention, and retry prevent transient refresh failures from freezing updates or mixing fee tiers

Monaco Protocol SDK v1.0.50

Breaking

  • Sweeper registration is authenticated and carries no identity — sdk.sweeper.register (POST /api/v1/sweeper/register; gRPC SweeperService.Register; Rust REST register_deposit_address) now requires a session, and clientId and userAddress are removed from the request: the application is the one the session was established under and the credited address is the session’s own, so a caller can only register their own address under their own application. Log in first and drop both fields — a body that still carries either is rejected (400) rather than ignored. Unauthenticated calls answer 401, an inactive application 403. The per-client rate limit becomes a service-wide admission limit on new registrations (429, safe to retry; re-registering an existing address is never limited) — honor the retryAfter value rather than assuming a window. The request gains an optional depositTarget ("spot" default, or "margin") that asks for the parent margin account’s collateral; it is part of the address derivation, so the two targets return two different deposit addresses and a "spot" address is never credited to margin. "margin" is a routing request, not a guarantee: a deposit that cannot be routed to collateral is credited to the spot wallet instead. An unrecognized value is rejected rather than falling back to spot

Changed

  • Funding an isolated risk bucket commits to the position’s margin by default — transferCollateralToRiskBucket (POST /api/v1/margin/risk-buckets/collateral/transfer-in; gRPC TransferCollateralToRiskBucket; the transfer_collateral_to_risk_bucket MCP tool) takes a new optional applyToPositionMargin, and the default is a behavior change. Omitted or true is the “Adjust Margin” gesture: the amount is committed to the isolated bucket’s sole open position’s stored margin — leverage drops and stays down, initial margin required rises, free collateral is unchanged. false is the previous plain allocation, and funding loops that top a bucket up to a free-collateral target must now pass it, or each top-up converts into requirement and the loop never converges. A flat isolated bucket is a plain allocation either way. On the cross form the contract differs by surface: raw REST and gRPC reject only an explicit true, while the TypeScript request type and the MCP tool schema reject the field however it is set — omit it entirely there rather than passing false. Either behavior improves the liquidation cushion identically. The response gains an optional positionMarginCredited — true the credit landed, false it degraded to a plain allocation because the persisted position row was stale, absent when no credit was in play

Deprecated

  • transferCollateralToMarginAccount — (POST /api/v1/margin/accounts/{margin_account_id}/collateral/transfer-in; gRPC TransferCollateralToMarginAccount) is deprecated in TypeScript (@deprecated) and in the OpenAPI operation (deprecated: true). It overloads two unrelated operations behind one route: with tradingPairId it allocates into that pair’s isolated risk bucket, without it it deposits from the wallet into the parent margin account. Use transferCollateralToRiskBucket for bucket funding — which now applies the same position-margin credit — and transferCollateralToParentMarginAccount for wallet-to-margin deposits. It keeps working; no removal date scheduled

Added

  • The deposit-address encoder is exported from @0xmonaco/contracts — predictSweeperAddress accepts an optional depositTarget ("spot" default, or "margin"), and encodeDepositApplicationData(clientId, target) plus the DepositTarget type are now exported. Omitting the target reproduces the previous derivation byte-for-byte; pass the same target the address was registered with, or the prediction points at the other ledger’s address. encodeDepositApplicationData is the canonical encoder for a deposit’s on-chain applicationData — the bytes a sweeper address is a hash of — so @0xmonaco/core consumes it internally and @0xmonaco/types re-exports DepositTarget from the same place rather than keeping second copies. The importable path is @0xmonaco/contracts; core’s package root does not re-export it. Both throw on a target that is not exactly "spot" or "margin" rather than treating an unrecognized value (the wire form "MARGIN", say) as spot and returning a real, fundable address for the wrong ledger

Fixed

  • marginRatio on the account WebSocket channel saturates on dust equity too — the documented contract of AccountEvent.marginRatio widens: the maximum-distress sentinel (79228162514264337593543950335) appears whenever a live maintenance requirement makes the ratio impossible to compute or represent, which now includes near-zero positive equity where the true maintenance / equity exceeds the decimal range, not only an account still owing one at zero or negative equity. An account with no maintenance requirement still reports 0, unchanged. The producer previously crashed on the dust-equity state instead of emitting a frame. No type change — clamp the sentinel for display and treat any value at or above the liquidation threshold as maximal distress

Monaco Protocol SDK v1.0.48

Breaking

  • ConditionalOrderEventData declares the values the server can really send — timeInForce widens from "GTC" | "IOC" to the full TimeInForce union (the field mirrors an unconstrained server column mapped exhaustively over every variant), and reason widens from the ConditionalOrderEventReason union to string because the wire field is a free-form server string. ConditionalOrderEventReason is still exported and still documents the canonical live values, now as a narrowing aid rather than a closed set. Code that switched exhaustively over either field needs a default branch to keep compiling. See Conditional Orders Channel

Added

  • availableOrderCollateral on risk-bucket rows — an optional field on the rows returned by getMarginAccountSummary, getParentMarginAccountSummary, and listMarginAccounts (GET /api/v1/margin/accounts/{margin_account_id}, GET /api/v1/margin/parent-margin-account, GET /api/v1/margin/accounts; gRPC MarginAccountsService), and on the riskBuckets rows from sdk.portfolio.getMargin() (GET /api/v1/accounts/me/portfolio/margin; gRPC AccountsService.GetPortfolioMargin). It is the collateral still available to open new orders in that bucket: the largest increase in the bucket’s worst-case order reserve the risk engine’s admission gate accepts, parent auto top-up included, and zero while frozen for liquidation or when an isolated bucket’s market is halted. Unlike withdrawableCollateral it credits an unrealized gain in full, and it already contains the bucket’s live free collateral — do not add freeCollateral or unrealizedPnl on top. Not additive across rows (every bucket’s figure includes the same parent headroom), and a requirement budget rather than an order size: a reducing, reduce-only, or sheltered order needs less of it or none, while an order crossing a losing position can need more than its own margin plus fees. Present only on rows served from a live risk engine snapshot; absent (key omitted, never null) on parent rows and persisted-fallback reads, and truncated toward zero at quote decimals on the portfolio surface. See Margin Accounts and Perps Collateral
  • useInstruments React hook — useInstruments(tradingPairId?) is a typed hook over the public instrument WebSocket channel. It subscribes through sdk.ws.instruments(), applies the subscribe-time snapshot and every subsequent change frame as a terminal-state entry per market, and unsubscribes on unmount. listing, delisting and config_change are the kinds emitted today; halt and unhalt are reserved for a future market-regime producer. Returns instruments (an InstrumentEventData[] ordered by symbol, then trading pair id), instrumentsByPair keyed by that id, plus subscribed, error and clearError. Omit the argument to follow every market; delisted markets stay in the list with isActive: false rather than disappearing. See useInstruments

Fixed

  • The conditionalOrders parser no longer drops whole frames on an unexpected value — an unexpected enum value in a conditional_order_update frame used to throw inside the WebSocket message handler and lose the entire update silently; a timeInForce of FOK or GTD, or any reason the server added, was enough. orderType, timeInForce, state, side, positionSide, conditionType, triggerSource, associationType and reason are now read leniently and passed through, matching the position, liquidation and market-stats parsers. event_type stays strict, so a mis-routed frame is still rejected. Match these fields with a default branch

Monaco Protocol SDK v1.0.47

Added

  • PASSIVE TWAP execution style — sdk.trading.createTwapOrder() takes an optional executionStyle ("TAKER" | "PASSIVE", default "TAKER" — the existing behaviour, unchanged). PASSIVE rests each slice as a post-only LIMIT GTC child at the same-side touch, chases the touch with reprices throttled to one attempt per second, and sweeps only the remainder as a banded MARKET child at the slot deadline; a target price that would cross executes immediately as a banded taker instead. The parent’s limitPrice is never breached, and neither is its slippageToleranceBps band wherever that band can be computed. One exception, and it matters if you rely on the band for protection: when the engine can resolve no protective reference at all — a one-sided book on a pair that has not traded in this process, so there is no mark, no trustworthy two-sided venue price and no last trade — the band check is skipped rather than failing closed, and the parent’s limitPrice becomes the only bound on the resting quote. Set limitPrice on a passive parent if you need a hard ceiling in a market that thin. The band is measured around the protective reference price: the oracle mark on a margin market when one is available, otherwise a robust book-derived venue price (from the best bid/ask, when the spread is not degenerate), falling back to the last trade — and spot parents always use that book-derived chain, since spot has no oracle mark. Passive parents plan on a 60 s target slice interval against the taker default of ~5 s, under the same 2,880-slice cap. Cancellation is cancel-only and atomic — a resting child is removed in the same step, earned fills stand, no final sweep. Passive maker fills pay the pair’s maker fee and no application fee; deadline sweeps and crossing takes pay application taker fees. executionStyle is on the read model, and the twap_orders WebSocket channel adds passive_child_placed, child_repriced, and passive_fill (sweeps still arrive as slice_placed). See Passive Execution
  • Conditional (price-triggered) TWAP parents — createTwapOrder() accepts triggerPrice + triggerDirection (ABOVE / BELOW, both-or-neither) to hold a parent PENDING until the reference price reaches the level, plus an optional conditionalExpiry (RFC 3339 UTC Z only, future, defaulted and capped at create + 30 days, valid only with a trigger) that terminalizes an unfired parent as CANCELLED with terminal reason trigger expired. A conditional create takes durationSeconds only — startTime/endTime are rejected — and runs a floating window anchored at activation. A trigger already satisfied at create is a 400, as is a conditional create on a margin market with no oracle mark yet; a waiting parent falls back to the venue reference price if the mark goes away. triggerPrice, triggerDirection, conditionalExpiry, and triggeredAt are on the read model and the WS payload, which adds the triggered and trigger_expired reasons. See Conditional TWAP
  • sequence on the account WebSocket channel — each frame carries a monotonic per-margin-account publish counter within one producer run. Dedupe by updatedAt (newest wins) and use sequence only to break ties between frames sharing an updatedAt. It may skip values and resets to zero on a producer restart (which can happen while the socket stays connected), so it is never a gap detector and never a cross-restart ordering mechanism

Changed

  • A crossing postOnly batch-replace item no longer costs the original order — batchReplace now runs the post-only crossing check in the pre-cancel preflight, against the book as it will stand once the batch’s own cancels are applied, so a refused item normally leaves its original resting with the same order ID and queue position instead of cancelling it. The item still reports error.code === "POST_ONLY_WOULD_CROSS"; only the fate of the original changed, bringing batchReplace into line with single replaceOrder. Four narrow cases remain, none visible before the cancel pass: crossing a replacement an earlier item just rested, crossing an original that failed to cancel, the same original listed twice, and an earlier non-post-only item that would have taken the touch. Every other per-item failure — insufficient balance, self-trade outside the batch, risk rejection — still leaves the original cancelled. See Batch Replace
  • Funding payments appear in the movements feed — GET /api/v1/accounts/movements (sdk.profile.getPaginatedUserMovements()) unions funding payments in at read time as funding transactions. Filter them with transactionType=FUNDING through the SDK — getPaginatedUserMovements() validates against an upper-case-only Zod enum, so a lower-case value throws before the request is sent; raw REST additionally accepts the lower-case transactionType=funding because the server matches case-insensitively. A funding movement is a collateral USD delta rather than a token transfer: amount is a magnitude with the direction in entryType (DEBIT paid, CREDIT received — REST movement rows keep the upper-case enum values), decimals is 0, and amountRaw equals amount. It shares its id with the live movements WebSocket frame so a client consuming both dedupes by id, but the payloads differ: REST is upper-case and resolves assetId/symbol/decimals, the WebSocket frame is lower-case with no resolved asset id. Zero-amount funding windows are excluded. See Account Balances
  • MonacoWebSocket and MessageHandler moved to @0xmonaco/types — both are now re-exported from @0xmonaco/core rather than defined there, making @0xmonaco/types the single source of truth. The public surface is unchanged and existing @0xmonaco/core imports keep working

Fixed

  • MonacoSDK.ws types the full channel surface — createMonacoSDK() instances carried a stale inline copy of the WebSocket type, hiding positions, liquidations, account, instruments, marketStats, and marketStatsAll from sdk.ws and rejecting ws.ohlcv’s optional priceType argument even though the runtime supported them. sdk.ws is now typed as the full MonacoWebSocket interface; runtime behaviour is unchanged
  • Corrected clientOrderId guidance — never retry on a lost response — the docs described the handle as “not de-duplicated and not unique” and told callers to reconcile a lost response against order history. Uniqueness is enforced, but only among a user’s resting orders: an order that never rests (MARKET, IOC/FOK, or a LIMIT that fills completely on arrival) never claims the handle, so a resubmission is accepted as a new order and can execute again, and the handle is released on any terminal state. Reconcile on the order WebSocket stream, which carries the handle even for orders that never rested — a matching event there is positive proof the order landed, while silence is never evidence it did not; order history is replica-backed and never contains an order rejected before acceptance. The MCP place_limit_order / place_market_order / replace_order descriptions carry the same correction, and MCP exposes no order-stream tool, so an agent must report an ambiguous outcome rather than retry. No wire, schema, or behaviour change

Monaco Protocol SDK v1.0.46

Added

  • TWAP transaction cost analysis — sdk.trading.getTwapOrderTca(twapOrderId) (GET /api/v1/orders/twap/{twapOrderId}/tca; gRPC OrdersService.GetTwapOrderTca) returns a post-execution TCA report for a terminal (COMPLETED / CANCELLED) TWAP parent: realizedAvgPrice (gross of fees) scored against the arrival price and the window-TWAP benchmark over the parent’s active window, with signed bps for both (slippageVsArrivalBps, performanceVsBenchmarkBps; positive = the execution beat the reference) and benchmarkSource naming the series (risk_mark_1m, or ohlcv_1m_close fallback). The six analytics fields are omitted from the body — key absent, not null — when they cannot be computed honestly; a non-terminal parent returns 400, and another user’s parent is a 404 identical to an unknown ID. @0xmonaco/types adds GetTwapOrderTcaResponse
  • Typed sdk.ws.instruments() WebSocket subscription — a typed helper for the public instrument channel shipped server-side in v1.0.45 (market lifecycle + config changes: listings, delistings, and tick/step/order-size/category edits; the perp-only leverage/margin-rate bounds are carried in every frame’s full config but are payload only — editing them alone does not emit a frame). Omit tradingPairId for all markets or pass one to filter; handlers receive the typed InstrumentEvent, every frame is a full-config terminal-state snapshot with changedFields, and the subscribe-time active-pair baseline is fanned out as one event per market before the live diffs (late subscribers on an already-held channel are replayed a locally cached baseline). Previously the channel was reachable only over a raw WebSocket connection

Monaco Protocol SDK v1.0.45

Added

  • Public instrument WebSocket channel — a public, unauthenticated channel streams trading-pair lifecycle + configuration changes (listings, delistings, and tick/step/order-size/category edits; perp leverage/margin bounds are carried in the frame but editing them alone is not a trigger). Subscribe to instrument:<tradingPairId> for one market or the bare instrument (instrument:*) for every market; each live Event frame carries one market’s full current config — consume it as a per-market snapshot, not a delta — with changedFields naming the touched fields, and the subscribe-time baseline arrives as a single Snapshot frame whose data is an array of those config objects. @0xmonaco/types ships InstrumentEvent / InstrumentEventData / InstrumentChangeKind; there is no typed sdk.ws helper, so consume the raw channel. See WebSockets
  • cumFees on positions — sdk.positions.listPositions() / getPosition() (GET /api/v1/positions, GET /api/v1/positions/{positionId}; gRPC PositionsService.ListPositions / GetPosition) add cumFees (signed decimal string) to Position: the position’s total lifetime trading fees — opening-fill fees plus every reducing execution — inclusive of the application-fee component, present on open and terminal rows. Unlike feesPaid (closing-side only), it is the whole-life figure; null when the opening sample or a reducing row’s fee is missing — but, unlike feesPaid, it is not gated on reducing-history completeness, so a rare entirely-absent legacy row can leave it non-null and understated

Fixed

  • Closed-position realized PnL and ROE net opening-fill fees — netRealizedPnl and realizedRoe on terminal positions now subtract the position’s opening-fill fees as well, not only its closing-side reducing-execution fees, whenever the opening PnL sample is available. Entry fees were previously ignored, mis-stating net realized PnL and the realized return; since opening-fill fees are signed the figures usually read lower but rise where those fills earned a maker rebate. Rows whose sample has aged out of PnL retention, or that predate the PnL service, keep the prior closing-execution net (with cumFees null)
  • Faucet history returns one entry per on-chain deposit — recentRequests (faucet info) now returns a separate minted entry for the spot and margin legs of a mint, each with its own amount (and a txHash once that leg confirms) and the margin leg labelled USDC (margin), instead of squashing a session into a single 2000 entry that carried only the spot transaction hash

Monaco Protocol SDK v1.0.44

Added

  • asErc20 deposit option for the wrapped-native asset — sdk.vault.deposit() accepts options.asErc20 (@0xmonaco/core; DepositOptions / DepositSchema in @0xmonaco/types) to deposit the chain’s wrapped-native asset (e.g. WSEI) as its ERC20 through approve + depositERC20, instead of the native-coin depositNative default from 1.0.43; sdk.vault.needsApproval() takes the same option and reads the real WSEI allowance. options is the fifth argument to deposit() and the third to needsApproval(); the ERC20 path is not auto-approved — deposit() throws a ContractError (code === "CONTRACT_ERROR", revertReason === "APPROVAL_REQUIRED") if the allowance is short
  • withdrawableCollateral on portfolio margin — sdk.portfolio.getMargin() (GET /api/v1/accounts/me/portfolio/margin; gRPC AccountsService.GetPortfolioMargin) adds withdrawableCollateral (decimal string) to PortfolioMarginTotal and each PortfolioRiskBucket (@0xmonaco/types): the largest amount that can actually leave the parent margin account / risk bucket, and the ceiling on funding new isolated / first-use positions. Unlike parentAvailableCollateral / freeCollateral it excludes unrealized gains and banked realized PnL — size transfers, withdrawals, and new isolated positions from it

Changed

  • replaceOrder’s quantity is the order’s new total — sdk.trading.replaceOrder() (PUT /api/v1/orders/{orderId}; gRPC OrdersService.ReplaceOrder) now treats newOrder.quantity as the order’s new total: a partially filled order rests total - filled and locks for that remainder only (FIX LeavesQty = OrderQty - CumQty), and the total must exceed the filled amount. Previously the value was placed as a fresh order size, so a 96/100-filled order replaced with quantity: "200" rested 200 — it now rests 104 and reads 96/200 filled. Omitting quantity keeps the total; unfilled orders and margin reduce-only replacements are unaffected; batchReplace items carry the same semantics. The replacement also inherits the original’s fill history (filled quantity, VWAP, realized fee / payment aggregates), so summing aggregates across a replacement chain double-counts. Types and wire shape are unchanged — this is a server-side semantics correction, so clients on any SDK version get it
  • Package metadata points at the public docs site — every published package drops the repository field and sets homepage to the public docs (the old private-repo links returned 404 outside the org): the five npm packages to https://docs.0xmonaco.com/sdk/typescript, and the Rust crates monaco-sdk / monaco-grpc-sdk to https://docs.0xmonaco.com/sdk/rust-rest / https://docs.0xmonaco.com/sdk/rust-grpc. No code or type changes — this is why @0xmonaco/react, @0xmonaco/contracts, @0xmonaco/mcp-server, and both Rust crates bump to 1.0.44 with no behavior change

Monaco Protocol SDK v1.0.43

Added

  • TWAP parent orders — submit one server-side TWAP parent and the matching engine slices it into market child orders across a time window. sdk.trading.createTwapOrder / getTwapOrder / listTwapOrders / cancelTwapOrder (POST /api/v1/orders/twap, GET /api/v1/orders/twap/{twapOrderId}, GET /api/v1/orders/twap, POST /api/v1/orders/twap/{twapOrderId}/cancel; gRPC OrdersService.CreateTwapOrder / GetTwapOrder / ListTwapOrders / CancelTwapOrder). Window is exactly one of durationSeconds or endTime, plus limitPrice / slippageToleranceBps / randomize / margin context / a clientTwapId idempotency key (duplicate → 409). Live progress streams on the authenticated twap_orders channel via sdk.ws.twapOrders(handler, tradingPairId?); @0xmonaco/types adds the full type set (TwapOrder, TwapOrderState, CreateTwapOrderOptions, TwapOrderEvent, …). Delegated-agent sessions are rejected. See TWAP Orders
  • In-place partial cancel — sdk.trading.cancelOrder(orderId, { remainingQuantityTarget }) (CancelOrderRequest.remainingQuantityTarget; gRPC remaining_quantity_target) reduces a resting order in place to the target remaining quantity, keeping its queue position — unlike replaceOrder, which rests a new order at the back. The target is idempotent under retries, a positive multiple of the quantity step, strictly below the current remainder. A new non-terminal OrderPartiallyCancelled event announces the reduction on the orders channel
  • Mark-price candles on the OHLCV WebSocket channel — sdk.ws.ohlcv(tradingPairId, tradingMode, interval, handler, priceType?) accepts an optional priceType ("trade" / "mark" / "*"). "mark" is the close-only mark-price series for margin pairs (open=high=low=close=mark, volume 0), published on the 1m interval only; the token is a fifth channel segment, so existing four-token subscriptions are unchanged. @0xmonaco/types adds OHLCVPriceType and an optional OHLCVEvent.priceType
  • wrappedNative balance flag — GET /api/v1/accounts/balances + /balances/{asset_id} (gRPC GetBalances / GetBalanceByAsset) return an optional wrappedNative boolean per balance (AccountBalance.wrappedNative), true for the chain’s wrapped-native asset (e.g. WSEI). It is the signal the vault client keys native-coin deposits on
  • Spot cost-basis fields on portfolio assets — sdk.portfolio.getAssets() (GET /api/v1/accounts/me/portfolio/assets; gRPC GetPortfolioAssets) surfaces four optional spot cost-basis fields per asset row — avgCost, unrealizedPnl, unrealizedRoe, cumRealized. Populated when a cost-basis checkpoint exists; unrealizedPnl / unrealizedRoe also require a mark price, and unrealizedRoe is omitted at zero cost basis

Changed

  • Vault deposits key native-coin handling on wrappedNative — the vault client decides native-coin handling from the balances API’s wrappedNative flag instead of a zero token address. sdk.vault.deposit() routes a wrappedNative asset (e.g. WSEI) to the payable depositNative with amount as msg.value — no ERC20 approval required or checked — while every other asset keeps approve + depositERC20; sdk.vault.needsApproval() returns false for it without reading the allowance; withdrawals of the wrapped-native asset pay out native coin (decided server-side)
  • Full-close TP/SL supersedes the previous full-close leg — attaching a full-close (closePosition: true) TP or SL via sdk.positions.attachPositionTpSl() (POST /api/v1/positions/{positionId}/tp-sl; gRPC AttachPositionTpSl) now cancels the position’s previously active full-close legs of the same condition type atomically in the same engine step (each old leg emits conditional_order_update reason: "cancelled" before the replacement’s "created"), so moving a level is one call with no both-or-neither window. OCO siblings, fixed-quantity legs, and entry-attached legs survive

Fixed

  • marginRatio for accounts at zero or negative equity — GetPositionRisk (GET /api/v1/positions/{positionId}/risk) and the account WebSocket channel (AccountEventData.marginRatio) now report the maximum-distress ratio for accounts at non-positive equity instead of 0 (equity exactly zero) or a negative ratio that sorted as safer than a healthy account. Accounts with no open positions still report 0. The sentinel is a very large decimal string (Decimal::MAX, ~7.9e28) — clamp it for display

Monaco Protocol SDK v1.0.42

Added

  • Typed market_stats and account WebSocket helpers — @0xmonaco/core gains sdk.ws.marketStats(tradingPairId, handler) and sdk.ws.marketStatsAll(handler) (public) and sdk.ws.account(handler) (authenticated), so the market_stats and account channels no longer need the raw WebSocket protocol. @0xmonaco/types adds MarketStatsEvent / MarketStatsData / MarketStatsAllEvent / MarketStatsSummary and the MarketStatus union; perp-only fields are optional so spot flows through the same helpers. The account producer stays gated off by default, so sdk.ws.account subscribes but frames arrive only once it is enabled
  • Mark-price candlestick series — the candlestick endpoint accepts an optional priceType (GetCandlesticksParams.priceType in @0xmonaco/core / @0xmonaco/types; the priceType query on GET /api/v1/market/pairs/charts/candlestick/{tradingPairId}/{interval}; GetCandlesRequest.price_type on gRPC MarketService.GetCandles). "trade" (default) is the trade-derived OHLCV series; "mark" is the close-only mark-price series for margin pairs, where each candle’s open/high/low/close are the minute’s closing mark and volume is 0. "mark" on a spot pair returns a 400

Changed

  • clientOrderId is unique among your resting orders — a single create or replace whose clientOrderId is already held by another of your own resting orders is rejected with 409 / gRPC ALREADY_EXISTS, code CLIENT_ORDER_ID_CONFLICT, naming the order already holding it (placeLimitOrder / placeMarketOrder / replaceOrder; POST /api/v1/orders, PUT /api/v1/orders/{orderId}; gRPC create/replace; the order MCP tools). In a batch (batchCreate / batchReplace) it is a per-item failure instead — the request returns 200 / OK with CLIENT_ORDER_ID_CONFLICT on the offending item’s results[].error.code, so inspect results[]. Where the conflict fires it is reliable — it names an order still resting under that handle — but its absence proves nothing, so never resubmit merely to probe for it: an attempt that filled on arrival, or was MARKET/IOC/FOK, holds no handle and the resubmission can execute again. Scope is per user among resting orders only; the value frees on terminal state and a replacement may restate it

Monaco Protocol SDK v1.0.41

Added

  • clientOrderId correlation handle on order placement — placeLimitOrder / placeMarketOrder / replaceOrder / batchCreate / batchReplace (@0xmonaco/core; POST /api/v1/orders, PUT /api/v1/orders/{orderId}, .../batch-create, .../batch-replace; gRPC CreateOrderRequest / ReplaceOrderRequest / BatchCreateOrderItem / BatchReplaceOrderItem; the place_limit_order / place_market_order / replace_order MCP tools) accept an optional clientOrderId (≤64 chars from A-Za-z0-9._:-), echoed on order detail/list reads and every orders WebSocket event. It is a correlation handle, not an idempotency key — not de-duplicated, and a replacement re-states it rather than inheriting it; invalid values are rejected with 400 / INVALID_ARGUMENT (batch-create per item with INVALID_CLIENT_ORDER_ID). Uniqueness was not enforced in this release; v1.0.42 added a guard, scoped to resting orders only
  • Order-attached TP/SL legs size independently — the takeProfit / stopLoss legs attached to an order at placement (ParentTpSlLeg) gain quantity and closePosition, the same sizing contract as position-attached TpSlLeg; previously they were frozen at the parent order’s quantity. Omit both (or closePosition: true) to track the live position, or name a quantity for a partial that never resizes
  • account WebSocket channel — a new authenticated channel (subscribe with account) for a margin account’s health — equity, collateral, free collateral, margin ratio, maintenance requirement (decimal strings) plus the margin account id and timestamp — via an account_update event. The channel routing and the AccountEvent type ship now, but the producer that emits frames is gated off by default and not yet enabled, so subscribing succeeds while no frames arrive yet. Raw-protocol only (no sdk.ws helper): the wire is snake_case (event_type, margin_account_id, free_collateral, …) and AccountEvent in @0xmonaco/types is the ergonomic shape nothing converts to on this channel. When enabled it is conflate lag policy with a periodic keepalive republish; treat each frame as a terminal-state snapshot
  • movements frames for on-chain deposits & withdrawals — deposits and withdrawals now emit a user_movement frame (transaction type deposit / withdrawal) co-ordered with their balance_update, so the movements and balances streams line up. The UserMovement wire shape is unchanged

Fixed

  • Funding an isolated risk bucket no longer ratchets a sole position’s margin — transferCollateralToRiskBucket() (@0xmonaco/core; transfer_collateral_to_risk_bucket MCP tool; REST POST /api/v1/margin/risk-buckets/collateral/transfer-in; gRPC TransferCollateralToRiskBucket) no longer credits the bucket’s sole open position’s stored margin — it always allocates reusable bucket collateral. This corrects the 1.0.40 migration note, which presented it as the addPositionMargin replacement; to increase a sole position’s stored margin use transferCollateralToMarginAccount(parentMarginAccountId, { asset, amount, tradingPairId }) instead

Monaco Protocol SDK v1.0.40

Breaking

  • addPositionMargin / reducePositionMargin removed — positions.addPositionMargin() / reducePositionMargin() (@0xmonaco/core), the perpRoutes.positions.addMargin / reduceMargin helpers, usePositions()’s two methods (@0xmonaco/react), the add_position_margin / reduce_position_margin MCP tools, REST POST /api/v1/positions/{position_id}/margin/add and .../margin/reduce, and the gRPC PositionsService.AddPositionMargin / ReducePositionMargin RPCs are all removed — they sourced collateral from the spot wallet, bypassing the parent margin account. Migration: allocate with transferCollateralToRiskBucket({ ..., marginMode: "ISOLATED" }), release with transferCollateralFromMarginAccount(parentMarginAccountId, { ..., tradingPairId })
  • WebSocket position status is SCREAMING_SNAKE_CASE — position events on the positions channel carry status as OPEN / LIQUIDATING / CLOSED / LIQUIDATED / EXPIRED, matching REST and @0xmonaco/types, instead of the previous PascalCase (Open). PositionEventStatus is narrowed; the event data also gains optional tradeId / orderId

Added

  • marginMode on collateral transfer-out — marginAccounts.transferCollateralFromMarginAccount() (TransferCollateralRequest; REST POST /api/v1/margin/accounts/{margin_account_id}/collateral/transfer-out; gRPC TransferCollateralFromMarginAccountRequest.margin_mode) accepts an optional marginMode. marginMode: "CROSS" addresses a cross risk bucket that tradingPairId cannot name; the two selectors are mutually exclusive
  • Dollarized open interest & 24h volume — getPerpMarketSummary gains openInterestBase, openInterestNotional, quoteVolume24h and getMarketMetadata gains quoteVolume24h; getScreener gains openInterestBase / openInterestNotional per ScreenerItem (both null for spot, "0" for a margin pair with no open positions). openInterest stays a base-asset alias
  • market_stats WebSocket channels — a public per-market channel (market_stats:<tradingPairId>: index/mark/mid, best bid/ask, open interest + limit, last trade, 24h stats, funding state) and a slower all-markets summary channel (market_stats / market_stats:*, with a market_status label). SPOT flows through both with perp-only fields omitted
  • Richer position / balance / movement events — position events now emit terminal LIQUIDATED (reason "liquidation" / "adl") on liquidation-worker & ADL closes plus counterparty OPEN refreshes, and a reason: "funding" position_update on funding settlement; the balance_update reason union gains fee / rebate / funding / margin_deposit / liquidation / adl and now fires for resting makers (with a separate rebate frame); funding surfaces on movements with transactionType: "funding"

Changed

  • Margin shortfalls no longer read “Insufficient balance” — InsufficientInitialMargin / InsufficientFreeCollateral now surface as insufficient initial margin: ... / insufficient free collateral: ... (400, type unchanged); genuine wallet shortfalls still read Insufficient balance. Match on the new messages to detect a margin (not wallet) shortfall
  • Candlestick endpoint honors its bounds — GET /api/v1/market/pairs/charts/candlestick/{tradingPairId}/{interval} (getCandlesticks) now enforces an absolute [startTime, endTime] window (inclusive, no padding) and treats limit as a hard cap that counts gap-filled bars. Defaults (350), clamp (500), and pagination direction are unchanged
  • Consistent order-event fill quantities — filledQuantity is always the cumulative order fill; a new optional lastFillQuantity / lastFillQuantityRaw carries the per-event increment; the IOC/market partial-cancel event now carries filledQuantity (legacy totalFilled retained as alias); cancelledAt is RFC3339 with Z and averageFillPrice is now typed
  • Batch requests capped at 100 items — BatchCreateOrdersSchema / BatchReplaceOrdersSchema reject arrays over 100 client-side (MAX_BATCH_ORDER_ITEMS exported from validation/trading); REST/gRPC reject an oversized batch with 400 At most 100 orders per batch request, and sequencer overload may return a retryable 503 OVERLOADED
  • tokenSymbol on deposit/withdrawal balance events — the balance_update event now carries the registry tokenSymbol on deposit / withdrawal (was null); type unchanged (string | null)

Fixed

  • WebSocket movement event parser — reads the serialized token key (with a token_address fallback) and surfaces status / assetId / blockNumber; UserMovementEventData now declares them (tokenAddress kept as a populated back-compat alias)
  • IOC position closes accept & require limitPrice — ClosePositionSchema and the close-position MCP tool now require limitPrice for LIMIT and IOC closes (forbidden only for MARKET), matching the server contract

Deprecated

  • TIME_IN_FORCE_GTD ("GTD") — declared in the protos but never implemented and always rejected; the enum value is retained for compilation and docs that advertised GTD are corrected to GTC. For a timed order send timeInForce: "GTC" with expirationDate

Monaco Protocol SDK v1.0.39

Breaking

  • latest_orders / latest_movements removed from paginated responses — GetPaginatedOrdersResponse and GetPaginatedUserMovementsResponse (@0xmonaco/types, surfaced through sdk.trading.getPaginatedOrders / sdk.profile.getPaginatedUserMovements) drop the dead latest_orders / latest_movements fields — a live-engine hot-cache shadow the backend stopped populating in v0.8.7, so callers have received undefined for them since. @0xmonaco/react’s useUserOrders / useUserMovements now seed initial state from the paginated orders / movements arrays; hook return shapes are unchanged. Migration: read the orders / movements arrays directly, and refetch a fresh snapshot on reconnect/resync

Added

  • Delegated-owner mode for the MCP server — @0xmonaco/mcp-server gains MONACO_DELEGATED_OWNER_USER_ID, a fail-closed startup option that runs the server on behalf of a delegated owner account. The MONACO_PRIVATE_KEY wallet is the agent: it authenticates as itself, then adopts an owner-scoped session (loginAsDelegatedOwner) so every tool call acts on the owner, with the agent wallet preserved as the audit actor. Requires MONACO_PRIVATE_KEY + MONACO_CLIENT_ID, rejects a blank value, and never falls back to trading as the agent’s own account. See Delegated owner mode

Fixed

  • useTokenLifecycle docs clarified to session-key vocabulary — the @0xmonaco/react hook’s JSDoc, inline comments, and log strings now describe the authenticated ed25519 session (persisted and refreshed via sdk.refreshAuth()) instead of implying JWT/bearer tokens; Monaco is a tokenless session-key exchange. Exported members and config fields keep their legacy “token” spelling, so the public API is unchanged, and the hook’s reference page is now linked from the docs navigation

Monaco Protocol SDK v1.0.38

Added

  • Typed positions & liquidations WebSocket streams — sdk.ws.positions(handler, tradingPairId?) and sdk.ws.liquidations(handler, tradingPairId?) (@0xmonaco/core) parse each frame into a typed PositionEvent / LiquidationEvent (@0xmonaco/types). Positions conflate (no gap signal); liquidations shed a slow subscriber with close code 1013 — treat it as the gap signal and resync from a snapshot. See WebSockets
  • WebSocket reconnect/resync on the SDK config — SDKConfig gains ws?: MonacoWebSocketConfig, so onStatusChange / onResync and the reconnect knobs (autoReconnect, maxReconnectAttempts, reconnectBaseDelayMs, connectionTimeoutMs) can be passed to new MonacoSDK({ ..., ws }) and are forwarded to the internal client — no longer only reachable through the low-level createMonacoWebSocket factory
  • postOnly on order reads — GetOrderResponse (order detail and each list item) echoes an optional postOnly boolean, so sdk.trading.getOrder / getOrders, REST GET /api/v1/orders / GET /api/v1/orders/{order_id}, and gRPC OrdersService.GetOrder / ListOrders now report whether an order was placed post-only. Absent for orders placed before the flag was persisted
  • Structured POST_ONLY_WOULD_CROSS code on single-order rejections — a single post-only order or replacement that would cross now carries the engine’s stable code on the REST error envelope’s code field and as a gRPC google.rpc.ErrorInfo reason, matching what the batch endpoints already returned. Match on the code; the message substring is now a legacy fallback
  • order_id filter on gRPC user trades — TradesService.ListUserTrades (authenticated, gRPC-only) adds an optional order_id UUID that returns only trades where that order placed the caller’s own side, across hot and archived history. Changing it invalidates an existing page token
  • PitPass React hooks — @0xmonaco/react adds useRewardsBalance, useTraderCodeInfo, and useTransferRewards, thin wrappers over the matching sdk.pitpass.* methods. See PitPass hooks

Fixed

  • withdrawableCollateral reports a withdrawable number — withdrawableCollateral, newWithdrawableCollateral, and marginAvailableCollateral (across the margin account / risk bucket summaries, transfer responses, and GET /api/v1/margin/available-collateral) now report the largest amount the withdrawal gate will accept instead of freeCollateral, which credited realized PnL, funding, and unrealized gains a transfer cannot move. Value-only — types, positions, and status codes are unchanged. Expect a smaller figure for any account or bucket carrying PnL, funding, or an unrealized gain, and size transfers from this field. Related fixes stop an unrealized gain funding a withdrawal, unblock a bucket-scoped transfer-out on a fully-allocated account, and prevent a piecemeal bucket withdrawal from billing a bucket’s loss to the insurance fund
  • WebSocket order-event field names — order events on ws.events.orders send tradingPair, filledQuantity, and fee, not the never-populated symbol / executionQuantity / makerFee; the RAW precision fields (quantityRaw, filledQuantityRaw, remainingQuantityRaw, totalFilledRaw) are now typed, and executedAt is RFC3339 with a Z suffix. useUserOrders reads its pair fallback from data.tradingPair
  • applications/config 401 description — corrected from “Unauthorized - Invalid or missing JWT” to “Authentication required”; the endpoint is session-authenticated and never used JWT. Description-only

Monaco Protocol SDK v1.0.37

Added

  • Post-only orders — postOnly?: boolean on sdk.trading.placeLimitOrder/replaceOrder/batchCreate/batchReplace (and the useTrade() equivalents), the REST create_order/batch_create_orders/replace_order/batch_replace_orders endpoints (POST /api/v1/orders, /batch-create, PUT /api/v1/orders/{order_id}, /batch-replace) and their gRPC OrdersService equivalents, and the MCP place_limit_order/replace_order tools. A maker-only guarantee, not a new order type: a LIMIT order using GTC/GTD that would cross the book is rejected instead of matching — REST 400 / gRPC InvalidArgument, message containing "post-only order would cross"; batch responses carry a structured error.code === "POST_ONLY_WOULD_CROSS" per item. Rejected on MARKET orders and when combined with IOC/FOK, and not persisted on the order. A single replaceOrder validates before cancelling the original (rejection leaves it resting); batchReplace is cancel-first (a rejected item’s original is already cancelled). See Post-Only Orders

Monaco Protocol SDK v1.0.36

Added

  • Registered deposit addresses with automatic sweeping — sdk.sweeper.register({ clientId, userAddress }) (POST /api/v1/sweeper/register; gRPC SweeperService.Register; Rust REST register_deposit_address) registers an (application, user) pair so its deterministic deposit address is monitored and every ERC20 deposit is swept into the rollup automatically. Public and keyless, idempotent and safe to retry; the reply carries the derived sweeperAddress and a registered/already_registered status, with the background one-time initial balance check reported by initialSweepCheck. @0xmonaco/contracts adds predictSweeperAddress(...) plus SWEEPER_ABI/SWEEPER_FACTORY_ABI to derive the same address off-chain
  • Human-readable asset display names on trading pairs — TradingPair gains baseAssetName/quoteAssetName (wire base_asset_name/quote_asset_name) on getPaginatedTradingPairs, getTradingPair, and getTradingPairBySymbol (GET /api/v1/market/pairs and /{trading_pair_id}; gRPC MarketService.ListTradingPairs/GetTradingPair) — e.g. "Robinhood Markets" for HOOD. Additive; use for name-based market search and display

Fixed

  • MCP order tools flagged non-idempotent — the place_limit_order, place_market_order, and replace_order tool descriptions now state order create/replace are non-idempotent and must not be blind-retried after an ambiguous outcome (a timeout, a network error, or a 5xx with no order returned); reconcile before resubmitting — corrected since: get_orders/get_order are replica-backed and omit an order that did land and never record one rejected before acceptance, so reconcile on the order WebSocket stream, which MCP has no tool for; an ambiguous MCP outcome must be reported, not retried
  • MCP tool schemas aligned with the SDK — get_trading_pairs/get_balances/get_movements now paginate with page/pageSize (not the ignored limit/offset) and get_trading_pairs gained a category filter; place_limit_order/place_market_order gained marginMode (ISOLATED/CROSS); the mandatory 1,000 bps market-order price band and the non-atomic, best-effort batch-cancel semantics are now described; every tool carries manifest-derived readOnlyHint/destructiveHint annotations

Monaco Protocol SDK v1.0.34

Added

  • Custom (vanity) PitPass TraderCodes — a wallet can claim a human-readable handle instead of the wallet-derived default: setTraderCode(code) (PUT /api/v1/pitpass/codes/me/custom; gRPC TraderCodeService.SetTraderCode), clearTraderCode() (DELETE /api/v1/pitpass/codes/me/custom), and the public advisory checkTraderCodeAvailability(code) (GET /api/v1/pitpass/codes/available). getMyTraderCode() gains customCode/isCustom/display; React adds useMyTraderCode/useSetTraderCode/useClearTraderCode/useTraderCodeAvailability and MCP adds set_trader_code/clear_trader_code/check_trader_code_availability
  • Lifetime cost fields on terminal positions — CLOSED/LIQUIDATED/EXPIRED rows from getPositions/getPosition (GET /api/v1/positions, /{positionId}; gRPC PositionsService.ListPositions/GetPosition) gain exitPrice (volume-weighted average exit), fundingPaid (lifetime funding, signed), and feesPaid (closing-side trading fees, signed); all three are omitted when the position’s reducing executions don’t cover its whole lifetime

Fixed

  • MCP server keeps its session alive — ensureAuthenticated is now expiry-aware (it refreshes when the session is within 60s of expiry, falling back to a fresh login if it is dead), so a long-running unattended agent no longer starts failing with 401s; idempotent read tools retry once on an auth-expired response, while order-placement and fund-movement tools are deliberately never auto-retried
  • MCP server starts cleanly on Node 18 — the bin polyfills WebCrypto from node:crypto when the global is missing, so @0xmonaco/core session-key generation no longer throws on the first authenticated call when launched via npx/node on Node 18

Monaco Protocol SDK v1.0.33

Breaking

  • Authenticated MCP sessions are scoped by a capability profile — MONACO_CAPABILITY (read | market-maker | full, default read) bounds which authenticated tools the MCP server registers; a private key no longer unlocks the full tool set. Order placement/cancel/replace require market-maker; fund-movement and privilege tools require full or an explicit MONACO_TOOL_ALLOWLIST entry. MONACO_NETWORK is now required when a private key is set (no more silent mainnet default), and MONACO_TOOL_ALLOWLIST/MONACO_TOOL_DENYLIST give per-tool overrides (denylist wins)

Added

  • 36 new MCP tools — the MCP server adds 36 tools covering perps, positions, portfolio, margin accounts, and related account/market data: perp market data (get_perp_market_config, get_perp_market_summary, get_mark_price, get_index_price, get_funding_state, get_open_interest, get_funding_history, get_all_funding_history), market (get_screener, get_market_stats), positions, portfolio, margin accounts (incl. the simulate_parent_margin_order_risk/simulate_risk_bucket_order_risk previews), get_user_trades, get_funding_payments, get_pending_withdrawals, and the public get_pulse feed. Reads are read-profile; close_position needs market-maker and the collateral-movement tools need full

Changed

  • Batch order replacement is cancel-first — sdk.trading.batchReplace/batchReplaceMarginLimitOrders (and Rust REST/gRPC batch_replace_orders) now cancel every item’s original before placing any replacement, in request order, so a two-sided quote can shift across its own resting prices without tripping self-trade prevention and a replacement can lock funds freed by an original in the same batch. Failure semantics change with it: an item whose replacement fails to place now reports the error with its original already cancelled (previously it stayed resting); validation failures and not-found originals are still left untouched
  • MCP tool inventory generated from a manifest — the README tool list derives from a single deterministic src/manifest.ts, with a CI drift gate that fails if the documented set diverges from the registered tools. No tool behavior changed

Fixed

  • Retry-After HTTP-date parsing on APIError — APIError.retryAfter now also parses the HTTP-date form of the header into remaining delay in seconds (past dates clamp to 0); all three RFC 7231 formats are accepted and malformed/impossible dates are ignored. The value is in seconds — multiply by 1000 for setTimeout
  • request_faucet MCP tool — fixed a dead tool that referenced removed accessToken wiring and always failed; it now calls sdk.faucet.mint with a Cloudflare Turnstile turnstileToken and stays network-restricted (never on mainnet)
  • Session-key auth in client JSDoc — the WebSocket client, React hooks (useUserBalances/useUserMovements/useUserOrders), and WebSocket balance/movement event types no longer reference JWT/token auth; they document the signed session-key Authenticate handshake (tokenless v0.8.8 model). Documentation-only; behavior and signatures unchanged

Monaco Protocol SDK v1.0.32

Added

  • Full-history iterators — sdk.trading.iterateOrders(params?) and sdk.profile.iterateUserTrades(params?) async generators walk full order and trade history (hot and archived), managing the pageToken cursor for you and yielding one row at a time
  • Multi-status order filter — getPaginatedOrders status accepts an array (OrderStatusFilter, e.g. ["SUBMITTED", "PARTIALLY_FILLED"]) sent as one request; a single status string still works
  • Completed position and funding types — PositionStatus adds terminal LIQUIDATED/EXPIRED plus a new PositionStatusFilter; FundingRecord now types the per-window fields (epoch, fundingDeltaPerUnit, referencePrice, sampleCount, windowStartedAt, windowClosedAt, settledAt)

Changed

  • Cursor pagination is now the default for getPaginatedOrders (GET /api/v1/orders) and getUserTrades (GET /api/v1/accounts/trades) — omitting page/pageToken starts a full-history cursor walk. Legacy offset pagination is deprecated (the page param, and total/page/totalPages, which read 0 in cursor mode); pageSize allows up to 1,000 in cursor mode. The same deprecations are annotated on the Rust REST/gRPC clients and the OpenAPI spec
  • React hooks and MCP tools follow the cursor default — useUserOrders/useProfile fetch via cursor (returned data unchanged, totals now 0); MCP get_orders gains pageToken (page deprecated)
  • Mainnet WebSocket ingress — the mainnet preset now uses wss://ws.apimonaco.xyz/ws (the previous wss://api.monaco.xyz/ws never resolved; custom wsUrl overrides are unaffected)

Fixed

  • Spot equity marks at the live trade print — portfolio spotEquity/totalEquity and the equity series (GET /api/v1/portfolio/stats, /portfolio/chart) now mark spot holdings at the last trade print instead of the lagging one-minute candle close; values shift accordingly and archived-only assets are priced instead of valued at zero. Perpetual positions were unaffected
  • First-use cross risk-bucket preview — POST /api/v1/margin/risk-buckets/simulate-order-risk (gRPC SimulateRiskBucketOrderRisk) no longer returns 400/INVALID_ARGUMENT for a first-use CROSS risk bucket; it previews the deterministic cross bucket virtually, funded from the parent margin account’s unallocated collateral

Monaco Protocol SDK v1.0.29

Added

  • Market pulse feed — new sdk.pulse.getPulse() (GET /api/v1/pulse, public) returns a cached news/sentiment feed: generatedAt (also the ETag), watchlist, and items[] (asset or "MARKET", sentiment, summary, updatedAt, sources[]). Hourly refresh with ETag/If-None-Match 304 and 503 until warm. Also gRPC PulseService.GetPulse
  • All-markets funding history — new sdk.market.listAllFundingHistory(params?) (GET /api/v1/market/funding/history, public) returns settled funding across every perp market in one call, with orderBy, page/pageSize, and startTime/endTime. Also gRPC MarketService.ListAllFundingHistory
  • Cursor pagination for orders and trades — getPaginatedOrders and getUserTrades accept a pageToken and return nextPageToken: omit it for legacy page numbers, pass "" to walk full history (hot and archived) newest-first, or pass a prior token to resume; a zero-row page with an empty token ends the walk
  • Funding fields — FundingRecord gains a carried flag; listFundingHistory accepts orderBy; ScreenerItem gains latestFundingRate and lastFundingTime (null for spot pairs and perps with no settled window yet)
  • Terminal position lifetime figures — terminal rows (CLOSED/LIQUIDATED/EXPIRED) report lifetime size and isolatedMargin, plus optional netRealizedPnl and realizedRoe

Changed

  • Market-order price band widened to 1,000 bps — the protective band is now 1,000 bps (10%; triggered TP/SL market legs use 1,200 bps), and market orders fill immediately-or-cancel (the quantity available inside the band fills, the remainder is cancelled; a zero-fill order still rejects). slippageToleranceBps only tightens the band, and the fee-simulation default market buffer follows to 1,000 bps
  • Entry-attached TP/SL re-enabled — takeProfit/stopLoss on placeLimitOrder/placeMarketOrder are accepted again for margin entry orders (direction follows side); spot and reduce-only orders still reject them
  • Position-based closed-trade win rate — win stats classify each position once by its net realized PnL; getRealizedPnl().closedTrades adds a positions total and getPortfolioStats().winLossRatio is position-based; count stays fill-based
  • Batch market orders reject timeInForce — matching the single-order endpoint; the SDK rejects it client-side before sending
  • Staging WebSocket ingress — the staging preset now uses wss://ws-staging.apimonaco.xyz/ws; the previous path still works during migration
  • Trade settlement status — the trade-level settlementStatus (REST/gRPC wire) retires SETTLED_ON_CHAIN; valid values are PENDING, SETTLED (terminal), and FAILED. The order OrderStatus enum is unchanged
  • Scoped maintenance mode — during maintenance, mutating operations are rejected with REST 503 OPERATIONS_BLOCKED (gRPC UNAVAILABLE + x-maintenance-blocked) while sign-in, order cancels, position closes, and risk simulations stay available

Fixed

  • Retryable 503 on matching-engine unavailability — close, tp-sl, pitpass/rewards/transfer, and the unauthenticated orderbook route return a retryable 503 instead of 500 when the engine is momentarily unreachable; non-transient faults still return 500

Monaco Protocol SDK v1.0.26

Added

  • Faucet quota introspection — new sdk.faucet.getInfo() (GET /api/v1/faucet/info, session-authenticated) returns the wallet’s remainingRequests24h, dailyLimit, optional nextAvailableAt, and up to five newest-first recentRequests. Wallet-scoped across apps, with the same rolling-window accounting as mint() enforcement; call it before requesting to avoid a 429. A matching get_faucet_info MCP tool ships for agents (non-mainnet only)
  • Position TP/SL full-close legs and OCO — attachPositionTpSl (POST /api/v1/positions/{positionId}/tp-sl) adds a per-leg closePosition flag (resolves the full live position size; omit quantity) and a request-level oco flag (cancels the sibling after one leg triggers; requires both legs)
  • Order cancellation cause — order responses and WebSocket order events expose a nullable, stable terminalReason (USER_REQUESTED, REPLACED, LIQUIDATION, INSUFFICIENT_MARGIN, REDUCE_ONLY_EXHAUSTED, POSITION_CLOSED, OCO_SIBLING_TRIGGERED, SYSTEM)
  • Base-quantity risk-tier cap — perp risk tiers add maxPositionSize (authoritative max absolute base-asset quantity); maxPositionNotional is now a deprecated alias
  • New SDK options — optional pollingInterval on createMonacoSDK, and maxReconnectAttempts / reconnectBaseDelayMs / connectionTimeoutMs / onResync on createMonacoWebSocket

Changed

  • One-way futures order semantics — side is the authoritative direction; positionSide is optional and deprecated (must agree with side when supplied); leverage is required for normal margin orders and optional for reduce-only; entry-attached TP/SL is rejected (400) — attach position TP/SL after the position opens
  • Market-order price protection — market orders execute inside a mandatory 500 bps server-side band around a robust reference price. Fills beyond the band are cut off (remainder cancelled), rejected as insufficient liquidity, or rejected as unavailable when there is no usable reference price. slippageToleranceBps can only tighten the band, never widen it. No wire types changed
  • WebSocket reconnect contract — auto-reconnect now retries indefinitely by default with full-jitter exponential backoff capped at 30s (was 5 attempts; pass maxReconnectAttempts for a finite cap), onStatusChange reports "reconnecting", the client answers a server Ping with a Pong, and a new onResync callback fires after each reconnect (slowClient: true on close code 1013) so apps can refetch snapshots and reconcile — corrected since: this release advised reconciling orders by (id, updatedAt), which cannot converge (order detail and order lists carry two different clocks). Reconcile on (id, version) instead — the counter is carried on the REST rows, on every order event, and on the orders snapshot frame — merging with >= (see Reconnect and resync)
  • Faster receipt polling — the internal viem client polls every 750ms by default (was 4000ms), speeding vault.deposit and waitForTransaction; set pollingInterval: 4000 to restore the old cadence
  • Faucet daily-limit counting — the daily limit now counts every mint call in the rolling 24h window rather than distinct clock minutes
  • Development WebSocket ingress — the development preset now uses wss://ws-develop.apimonaco.xyz/ws; the previous path still works during migration

Fixed

  • Batch-replace taker fees — the PnL service now attributes the taker fee on a batch-replace fill to its owning margin account (batch replace carries no top-level taker order, so those fees were dropped), correcting 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 key now returns 409 Conflict / gRPC already_exists instead of a 500 that leaked Postgres detail; generate a fresh session keypair per session
  • Hardened error envelopes — database failures return a generic 500 “Database error” and malformed JSON returns 400 “Invalid JSON payload”, with no engine detail, constraint names, or query text echoed
  • Withdrawal-index validation — GET /api/v1/withdrawals/{withdrawal_index} rejects a negative or non-integer index with 400 instead of a misleading 404; valid lookups are unchanged

Monaco Protocol SDK v1.0.24

Fixed

  • Isolated-margin partial-fill collateral returns to the parent account — cancelling (or IOC-releasing) the unfilled remainder of a partially filled isolated-margin limit order now returns the excess auto-funded risk-bucket collateral to the parent margin account’s free collateral immediately. Previously the full order-sized allocation stayed locked in the bucket until the position closed, inflating bucket equity and deferring liquidation beyond the isolated margin
  • reducePositionMargin rejects below-maintenance reductions — POST /api/v1/positions/{positionId}/margin/reduce (gRPC PositionsService.ReducePositionMargin) now returns 400 / INVALID_ARGUMENT when a reduction would drop the position below its maintenance requirement, with the maintenance floor, post-reduction margin, and maximum removable amount in the error. Previously margin could be reduced to zero, after which the engine silently re-margined the position at the pair’s maximum leverage
  • newIsolatedMargin reports post-transfer position margin — the newIsolatedMargin field in the add/reduce position-margin responses now reflects the position’s margin after the transfer instead of the risk bucket’s allocated principal, which could include unrelated bucket equity

Monaco Protocol SDK v1.0.21

Added

  • Portfolio analytics API — new sdk.portfolio namespace with getSummary, getPerformance, getRealizedPnl, getAssets ({ period } of 1d/1w/1m/all, default all), and current-only getMargin(). Backed by authenticated GET /api/v1/accounts/me/portfolio/* and gRPC AccountsService.GetPortfolio*, returning authoritative Core calculations — transfer-neutral sampled TWR, explicit history coverage, spot/perp/funding/fee realized components, asset-ID-priced holdings, and separately scoped parent/risk-bucket margin. See the Portfolio Analytics guide

Removed

  • Standalone backend authentication (breaking) — POST /api/v1/auth/backend (authenticate_backend), gRPC AuthService.BackendAuth, and the BackendAuthRequest/BackendAuthResponse messages are removed from the wire contract and generated SDKs. Backend applications continue to authenticate each request with their sk_ secret key in the x-server-key header — set it once with sdk.setServerKey("sk_..."). The TypeScript authenticateBackend() helper was already removed in v0.8.10; only callers of the raw endpoint or RPC need to migrate

Fixed

  • Portfolio stats fee totals — getPortfolioStats (GET /api/v1/accounts/me/portfolio) now sources pnlBreakdown.fees and totalPnl from the authoritative trade-ledger fee total (same source as feesPaid), not the PnL service’s cumulative counter, which under-recorded margin fills whose order carried no margin position id. Because stats read the ledger, the fix applies across all history; the headline pnl was already correct and is unchanged
  • Portfolio chart fee metrics — getPortfolioChart reads persisted per-account fee snapshots, and those are now recorded correctly: every margin fill’s fee is attributed to its owning margin account (including no-position-id closing/reducing fills) and includes the application_taker_fee component. This corrects the chart’s fees, realizedPnl, and totalPnl series for data recorded after the fix
  • SDK sends an explicit User-Agent header — @0xmonaco/core now sets a User-Agent header on every REST request. Monaco’s edge rejects an empty or absent User-Agent, so SDK traffic could intermittently be blocked; the header is ignored in browsers, where it is forbidden

Monaco Protocol SDK v1.0.16

Added

  • Exact tier fee rates on fee simulation — fees.simulateFees (GET /api/v1/fees/simulate, gRPC FeesService.SimulateFees) now returns monacoTakerFeeBpsExact and monacoMakerRebateBpsExact — decimal basis-points strings carrying the tier-resolved rate the ledger actually charges (e.g. "6.5", negative for a rebate), equal to monacoTakerFee / notional. An integer cannot represent fractional per-user tiered rates, so these replace the deprecated integer fields below
  • Optional faucet captcha token — faucet.mint() accepts an optional turnstileToken (REST body field / gRPC MintTokensRequest.turnstile_token) for Cloudflare Turnstile. Omitting it keeps today’s behavior; a valid token becomes required once the gateway enables faucet captcha enforcement

Changed

  • Order-management endpoints return the correct HTTP status on rejection — place/cancel/replace/batch orders and conditional orders now map matching-engine client-fault rejections (e.g. an invalid TP/SL trigger price) to their proper status instead of 500: invalid_argument/out_of_range/resource_exhausted → 400, not_found → 404, failed_precondition/already_exists → 409, permission_denied → 403, unauthenticated → 401. Genuine matching-engine faults still return 500
  • Rust REST SDK builds on native-tls — the published monaco-sdk crate now builds its reqwest client on native-tls instead of rustls, matching the rest of the workspace. Consumers on platforms without a system TLS library (e.g. static musl builds) now need OpenSSL available at build time

Deprecated

  • Integer and flat fee-bps fields — the integer monacoTakerFeeBps / monacoMakerRebateBps on the fee-simulation response, plus the flat makerFeeBps / takerFeeBps on trading-pair and account responses, are deprecated. They echo the pre-tiered pair-level rate, not the per-user tiered fee. Use the exact fields above, or GET /api/v1/fees/tier / fees.getMyFeeTier, for the caller’s real rate

Monaco Protocol SDK v1.0.7

Changed

  • faucet.mint() now confirms on-chain before resolving — previously resolved with a partial-success body inspected via minted/failed. It now resolves only when every faucet transaction confirms on-chain, and throws otherwise: 429 when the daily quota is exhausted, 502 when mints failed or only partially confirmed (the error carries the minted/failed breakdown; delivered legs still consume the quota), and 503 when the faucet is temporarily unavailable (safe to retry). Move partial/failure handling into a try/catch; on a successful resolve failed is empty and the MintTokensResponse shape is unchanged

Fixed

  • Balances re-fetch is consistent after a live update — re-fetching balances immediately after a balances WebSocket event (sdk.profile.getUserBalances(), sdk.profile.getUserBalanceByAssetId(), or GET /api/v1/accounts/balances / gRPC AccountsService.GetBalances) now reflects that update instead of occasionally returning stale data; no method arguments or response fields changed

Monaco Protocol SDK v1.0.3

Added

  • Closed position execution history — positions.listPositionHistory accepts a new reductionOnly boolean to filter down to reductions, closes, and liquidations only. Each reduction row now includes backend-computed close facts: entryPrice, allocatedInitialMargin, fundingPaid, netRealizedPnl, realizedRoe, and positionSide — no mark-price approximation needed for realized return display

Changed

  • Conditional cross liquidation prices — listPositions, getPosition, and getPositionRisk now return liquidationPrice for cross positions (conditional: only the target position’s mark moves, other marks in the same risk bucket held fixed). Risk simulations (simulateOrderRisk, simulateParentMarginOrderRisk, simulateRiskBucketOrderRisk) now return freeCollateralAfter and optional estimatedLiquidationPrice?; all three accept an optional reduceOnly boolean; cross simulation requires selectedTradingPairIds

Fixed

  • vault.withdraw with autoWait: false blocked instead of returning early — previously polled up to 5 minutes for a merkle proof that is never ready at initiation, leaving the balance stuck and the wallet prompt never shown; now returns { withdrawalIndex, status: 'awaiting_proof' } immediately; call retryWithdrawal(withdrawalIndex) once the proof is confirmed
  • fees.getMyFeeTier volume fields now in whole USD — weightedVolume14d, spotVolume14d, and perpVolume14d previously returned raw micro-USDC values (10⁶× too large), causing every user to resolve to a higher fee tier than warranted; all three fields now report whole-USD amounts

Monaco Protocol SDK v1.0.2

Added

  • PitPass TraderCodes — every Monaco wallet now has a unique referral code. Share it to earn a share of the net protocol fee generated by referred users’ trades — up to three levels deep (30% / 15% / 5%). New SDK surface: sdk.pitpass.getMyTraderCode(), getTraderCodeInfo(code), getRewardsBalance(), transferRewards({ token, amount }). sdk.auth.authenticate() accepts an optional referralCode argument to record the referral relationship at sign-up
  • Tiered fees — fee tiers applied per-user based on a 14-day rolling weighted volume (14d perp + 2.5 × 14d spot). Six tiers; volumes recomputed daily at 00:00 UTC. Current tier and next-tier volume returned by GET /api/v1/fees/tier (sdk.fees.getMyFeeTier)
  • Fee treasury — net protocol fees credited to a dedicated treasury at fill time; funds PitPass rewards without minting new tokens

Monaco Protocol SDK v0.8.22

Added

  • Quantity step size on trading pairs — TradingPair.quantityStepSize (string) is now returned by market.getTradingPair, market.getPaginatedTradingPairs, and GET /api/v1/market/pairs; the base-token lot-size increment used to validate order quantity precision
  • Unrealized PnL and breakdown — getPortfolioStats gains unrealizedPnl, totalPnl, and pnlBreakdown (spotRealized, spotUnrealized, perpsRealized, perpsUnrealized, fundingPaid, fees); getPortfolioChart accepts six new metrics: unrealized_pnl, total_pnl, realized_pnl, funding_paid, fees, equity
  • Position PnL history — new GET /api/v1/positions/{positionId}/pnl/history endpoint (positions.getPositionPnlHistory(id, { interval, startTime?, endTime? }), usePositionPnlHistory hook, gRPC PositionsService.GetPositionPnlHistory) returns bucketed PnL samples on the 1m/5m/15m/1h/4h/1d interval ladder

Fixed

  • Per-position margin requirements — listPositions, getPosition, and getPositionRisk now return position-scoped initialMarginRequired and maintenanceMarginRequired instead of the parent margin-account aggregate; no field-shape changes

Monaco Protocol SDK v0.8.21

Added

  • Get conditional order by ID — new GET /api/v1/orders/conditional/{conditionalOrderId} endpoint (sdk.trading.getConditionalOrder(id), useTrade().getConditionalOrder(id), gRPC OrdersService.GetConditionalOrder) fetches a single TP/SL conditional order by its UUID — the same IDs returned as takeProfitOrderId and stopLossOrderId in the createOrder response

Changed

  • Realized PnL accounting semantics — getPortfolioStats (pnl) and getPortfolioChart ("pnl" metric) now apply updated cost-basis semantics: spot withdrawals are booked as fair-value disposals; deposits and withdrawals are marked at the last observed trade price (not the covering 1-minute candle close); transfers before the first trade on a pair are PnL-neutral; short-entry fees reduce short profits instead of inflating them. Response shape unchanged

Fixed

  • Taker order fee fields reported flat rate instead of tier rate — a filled order’s monacoTakerFee, totalTakerFees, and takerTotalPayment now report the tier-resolved rate actually debited at settlement, not the trading pair’s flat fee column
  • Cross-margin simulation failed before first cross order — simulateRiskBucketOrderRisk with marginMode: "CROSS" now works before the user’s cross risk bucket has been initialized (previously returned “No cross risk bucket found”); uses the same deterministic bucket ID and auto-collateral estimate as live order placement
  • Ambiguous parent margin-account orders now fail fast — POST /api/v1/orders and batch-create for a parent margin-account order that omits marginMode and no isolated risk-bucket selector now return a client error instead of silently routing through the isolated path; pass marginMode: "CROSS" or marginMode: "ISOLATED" explicitly

Security

  • conditional_orders WebSocket now requires auth at subscribe time — the sdk.ws.conditionalOrders channel now gates at subscribe (not just delivery); unauthenticated subscribe attempts are rejected

Monaco Protocol SDK v0.8.20

Changed

  • Cross margin auto-resolved on order placement — POST /orders and POST /orders/batch-create with marginMode: "CROSS" now automatically provision or expand the cross risk bucket when needed. When no active cross bucket exists, the API resolves the deterministic cross bucket and estimates required collateral; when one exists but doesn’t cover the requested tradingPairId, that pair is auto-added. No request or response fields changed
  • Sub-account spending limit unauthorized read returns 404 — GET /api/v1/accounts/sub-accounts/{subAccountId}/limits for a sub-account belonging to a different master now returns 404 instead of 403, matching the create/update/delete behavior and making an unauthorized read indistinguishable from a nonexistent sub-account

Fixed

  • balances WebSocket missing spot-fill updates — sdk.ws.balances() now emits a trade-reason balance update to both counterparties when a spot order fills. Previously only deposits, withdrawals, and collateral transfers pushed balance updates, leaving live balance state stale after spot trading
  • simulateFees used flat fee rate instead of your tier rate — sdk.trading.simulateFees now returns your tier-resolved taker fee (monacoTakerFee, totalTakerFees, takerTotalPayment) — the rate the ledger actually debits — instead of the trading pair’s flat rate, which under-quoted the fee at any tier whose rate exceeds the flat column
  • Isolated margin replace to lower notional left collateral stale — replacing an isolated margin order with a lower notional now immediately returns the freed collateral to the parent account. getMarginAccountSummary, getParentMarginAccountSummary, and getAvailableCollateral now reflect the correct freeCollateral right after the replace; previously the bucket’s allocated collateral stayed unchanged until the order was cancelled or filled

Monaco Protocol SDK v0.8.19

Added

  • Cross margin on risk buckets — margin orders can now target a shared cross risk bucket spanning several pairs, not just per-pair isolated buckets. placeLimitOrder/placeMarketOrder/batchCreate accept an optional marginMode ("ISOLATED" | "CROSS", default ISOLATED); "CROSS" requires marginAccountId and routes to the active cross bucket for the pair. Position reads expose marginMode, and MarginAccountSummary exposes selectedTradingPairIds on cross rows. transferCollateralToRiskBucket and simulateRiskBucketOrderRisk accept marginMode/selectedTradingPairIds and echo the resolved bucket. Additive — marginMode defaults to ISOLATED, shapes unchanged

Monaco Protocol SDK v0.8.18

Fixed

  • spotEquity mark-priced — getPortfolioStats().spotEquity (and totalEquity, which includes it) now value spot balances at current mark price instead of summing raw token quantities as if each asset were worth one dollar. Response shape unchanged; values corrected

Added

  • Fee-tier lookup — new GET /api/v1/fees/tier (sdk.fees.getMyFeeTier, useFees().getMyFeeTier, MCP get_my_fee_tier) returns your resolved fee tier (1–6), rolling 14-day weighted/spot/perp volumes, the volume still needed to reach the next tier, and a trading pair’s six-row maker/taker fee schedule

Monaco Protocol SDK v0.8.16

Fixed

  • Portfolio realized PnL — getPortfolioStats().pnl and getPortfolioChart(period, "pnl") now return average-cost realized PnL (booked on position reduce/close, net of fees, spot + perps incl. funding, excludes unrealized) instead of net trading cash-flow. Response shape unchanged; values corrected
  • winLossRatio type — PortfolioStats.winLossRatio is now number | null (was string), matching the API; null when the period has no closed trades

Added

  • Spot / perps equity breakdown — PortfolioStats gains spotEquity and perpsEquity (quote-normalized strings) alongside totalEquity

Changed

  • totalEquity includes perpetuals — now spot balances plus perps (margin) account equity, not spot alone
  • maxDrawdown nullable — PortfolioStats.maxDrawdown is now string | null (null when no closed trades)

Monaco Protocol SDK v0.8.15

Breaking

  • REST wire format is now camelCase — every REST request/response JSON field and path parameter switches from snake_case to camelCase (trading_pair_id → tradingPairId, created_at → createdAt, page_size → pageSize, /withdrawals/{withdrawal_index} → /withdrawals/{withdrawalIndex}, …). The TypeScript SDK already speaks camelCase, so SDK consumers just bump the version; direct REST callers must rename fields and path params. The WebSocket raw wire and gRPC are unchanged

Added

  • List pending withdrawals — sdk.withdrawals.listPendingWithdrawals({ page?, pageSize? }) (GET /api/v1/withdrawals) returns the caller’s pending (awaiting-proof) withdrawals, newest first and paginated; each PendingWithdrawal carries withdrawalIndex, assetId, assetSymbol, amount, destination, status, createdAt. Also in @0xmonaco/react via useVault().listPendingWithdrawals

Monaco Protocol SDK v0.8.14

Removed

  • Idless “auto margin account” endpoints removed — transferCollateralToAutoMarginAccount (POST /api/v1/margin/collateral/transfer-in) and simulateAutoMarginOrderRisk (POST /api/v1/margin/simulate-order-risk) now 404. Use transferCollateralToParentMarginAccount / simulateParentMarginOrderRisk (or the new risk-bucket methods)

Added

  • Parent-margin-account & risk-bucket collateral API — explicit idless endpoints replace the auto shortcut: getParentMarginAccountSummary, transferCollateralToParentMarginAccount/From, getParentMarginAccountMovements, simulateParentMarginOrderRisk, plus per-pair transferCollateralToRiskBucket and simulateRiskBucketOrderRisk
  • Batch close all positions — sdk.positions.batchCloseAllPositions({ tradingPairId?, slippageToleranceBps? }) (POST /api/v1/positions/batch-close-all) MARKET-closes every open perp; best-effort with per-position results. Also in @0xmonaco/react and the batch_close_all_positions MCP tool
  • Raw total balance — balance responses gain total_balance_raw (raw integer string, available + locked) alongside normalized total_balance
  • fx asset class — Pyth-backed FX perps add category: "fx" to trading-pair filters and responses

Changed

  • margin_bucket_* → risk_bucket_* rename (breaking for REST/SDK) — order options marginBucketId/marginBucketCollateral → riskBucketId/riskBucketCollateral, and the Position response field margin_bucket_id → risk_bucket_id. gRPC field numbers unchanged; JSON keys change
  • Withdrawals migrated to merkle-proof executeWithdrawal (signed path dropped) — withdraw/retryWithdrawal signatures unchanged, but a v0.8.11 SDK breaks against the v0.8.14 backend, so frontends must upgrade. On proof-poll timeout they now resolve to { withdrawalIndex, status: "awaiting_proof" } instead of throwing (finish via retryWithdrawal); WithdrawResult.status adds "awaiting_proof"; default timeout is 5 min (retry.timeoutMs). withdraw(…, "margin") can source from the parent margin account
  • Address-shaped return types are now viem Address — SDK address outputs (getAccountAddress(), vault.getVaultAddress(), address fields on responses/events) are typed 0x${string} instead of string. Type-only; inputs unchanged
Read full release notes →

Monaco Protocol SDK v0.8.11

Removed

  • CreateConditionalOrder gRPC RPC removed — the method had returned FAILED_PRECONDITION since it was deprecated in v0.8.7. Attach TP/SL at CreateOrder or use AttachPositionTpSl / sdk.positions.attachPositionTpSl(...)

Added

  • Asset-class category on trading pairs — TradingPair and ScreenerItem gain category: "crypto" | "equities" | "commodities"; getPaginatedTradingPairs and getScreener accept an optional category filter
  • Life-to-date market stats — sdk.market.getMarketStats() returns exchange-wide cumulative total_quote_volume_ltd / total_trade_count_ltd; MarketMetadata and ScreenerItem gain per-pair LTD totals
  • Deposit directly to margin — vault.deposit(assetId, amount, autoWait?, "margin") routes the on-chain deposit straight into margin collateral, skipping the separate transfer-in step

Fixed

  • Parent margin account collateral transfers — transferCollateralToMarginAccount/From now stay scoped to the parent when tradingPairId is omitted, even when an active cross-risk bucket exists
  • getAvailableCollateral now correctly surfaces parent margin account free collateral
Read full release notes →

Monaco Protocol SDK v0.8.10

Breaking Changes

  • Margin account provisioning is now implicit: POST /api/v1/margin/accounts and the createMarginAccount / ensureParentMarginAccount methods are removed. The parent margin account is created automatically on the first accountless parent collateral transfer-in — use sdk.marginAccounts.transferCollateralToParentMarginAccount(...)
  • Standalone conditional-order creation removed: createConditionalOrder (core + React) and POST /api/v1/orders/conditional are gone. Attach TP/SL at order placement or use sdk.positions.attachPositionTpSl(...). Listing/cancelling conditional orders is unchanged
  • GET /api/v1/trades/user removed from REST (now gRPC-only); use GET /api/v1/accounts/trades
  • Rust REST SDK: auth refresh method renamed refresh_token → refresh_session

Added

  • Withdrawals client (sdk.withdrawals.initiateWithdrawal / getWithdrawal) returning signed calldata; sdk.vault.withdraw stays the high-level broadcast flow
  • Delegated-agent owner reverse-lookup (listDelegatedOwners); the full delegated-agents surface is now in the OpenAPI spec and gRPC SDK
  • New surfaces: sdk.subAccounts, sdk.faucet, sdk.profile.listFundingPayments, and backend application reporting (listApplicationOrders / Users / Movements / Balances, getApplicationStats)
  • Market: getTradingPair, getScreener. Trades: getTradeById
  • gRPC AccountsService GetUserTrades / GetPortfolioStats / GetPortfolioChart are now implemented
  • Generated @0xmonaco/types/wire namespace; Rust REST SDK resynced (~38 new methods)

Changed

  • REST and gRPC now share one set of handlers — gRPC behavior is aligned to REST (error codes, decimal precision, normalized amounts). GetMarketMetadata no longer returns live mark_price / index_price (use getMarkPrice / getIndexPrice)
  • createDelegatedSession now requires sessionPublicKey and its response no longer includes access_token
  • executed_at on user trades now uses an explicit +00:00 offset instead of Z (any ISO 8601 parser is unaffected)
Read full release notes →

Monaco Protocol SDK v0.8.8

Breaking Changes

  • Authentication moves to ed25519 session-key signing: JWT access/refresh tokens are replaced by wallet-authorized session keys. AuthState now carries sessionPublicKey / sessionPrivateKey instead of accessToken / refreshToken; sdk.auth.refreshToken / revokeToken become refreshSession / revokeSession (use sdk.refreshAuth() / sdk.logout()); requests are signed automatically. All existing sessions are invalidated on deploy — users must reconnect and sign once

Added

  • Custom API / WebSocket URLs: SDKConfig gains optional apiUrl / wsUrl overrides for self-hosted or CI/e2e stacks; network still selects the chain
Read full release notes →

Monaco Protocol SDK v0.8.7

Fixes

  • withdrawable_collateral now reflects live free collateral: MarginAccountSummary.withdrawable_collateral and the new_withdrawable_collateral field on transfer-in/out responses return the risk-bounded free collateral amount instead of equity-based available_margin. Safe to pre-fill into a “Max withdraw” button. Affects getMarginAccountSummary, listMarginAccounts, transferCollateralToMarginAccount, transferCollateralFromMarginAccount
  • Transfer-out and isolated margin reduction use margin state directly: transferCollateralFromMarginAccount and sdk.positions.reducePositionMargin no longer depend on a wallet-side margin collateral mirror. No SDK signature changes
  • Open interest values are now correct: sdk.market.getOpenInterest() is sourced from live margin positions instead of OHLCV candles (where the column was never populated). OpenInterest also gains an optional open_interest?: string base-units alias for backward compat

Changes

  • latest_orders and latest_movements always undefined: With Redis removed from the orders/movements path, the optional Redis hot-cache shadow fields on getOrders() and getUserMovements() are no longer populated. Both remain on the response interface as optional — switch to the paginated orders / movements arrays
Read full release notes →

Monaco Protocol SDK v0.8.5

New Features

  • App Statistics (sdk.applications.getApplicationStats): Backend-auth only. Returns aggregate volume, maker_fee, taker_fee, application_taker_fee, and trade_count for trades where the application’s users were the taker. Optional since param (ISO 8601) for time-ranged queries. Also available as ApplicationsService/GetAppStats via gRPC
  • transferCollateralToRiskBucket: Fund the auto-resolved risk bucket for a (tradingPairId, strategyKey) pair — no marginAccountId lookup required
  • simulateRiskBucketOrderRisk: Preflight a perp order against the auto-resolved risk bucket for a (tradingPairId, strategyKey) pair
  • listMarginAccounts filter: New optional tradingPairId param to filter accounts by trading pair
  • MarginAccountSummary fields: trading_pair_id and strategy_key now returned in list and summary responses
Read full release notes →

Monaco Protocol SDK v0.8.4

New Features

  • Delegated Agents (sdk.delegatedAgents): Authorize a separate signer wallet to submit orders on behalf of an owner account. The owner retains full custody; the agent is constrained to the trading actions, pairs, order types, leverage, and notional limits defined in its policy
  • upsertDelegatedAgent: Register or update an agent policy — allowed actions, trading pairs, order types, time-in-force, max leverage, max order notional, expiry
  • listDelegatedAgents: List all active delegated agents for the authenticated owner
  • revokeDelegatedAgent(agentId): Revoke an agent policy; future policy-gated trading fails even if a delegated auth session has not expired
  • createDelegatedSession({ ownerUserId }): Agent exchanges its own authenticated session for a short-lived, owner-scoped delegated session — orders then settle against the owner’s margin accounts
  • strategyKey on order placement: Optional field on placeLimitOrder, placeMarketOrder, batchCreate, and batchReplace. Monaco resolves the parent margin account and targets the isolated bucket for the (user, tradingPair, strategyKey) combination — recommended for delegated agent flows and strategy-segregated margin accounting
No breaking changes. Read full release notes →

Monaco Protocol SDK v0.7.9

New Features

  • Parent TP/SL at order placement: Pass takeProfit and/or stopLoss options directly on placeLimitOrder / placeMarketOrder. Legs inherit the parent order’s quantity, activate on fill, and OCO-cancel each other. Margin-only
  • CreateOrderResponse.take_profit_order_id / stop_loss_order_id: IDs of the attached conditional legs, returned on placement
  • PENDING_PARENT conditional order state: New state for legs waiting on an unfilled parent order. Cancelling the parent auto-cancels pending children
  • ConditionalOrder fields: parent_order_id, association_type, linked_group_id, activated_at added to list and websocket responses

Changes

  • conditional_order_update websocket event: New fields parentOrderId, associationType, linkedGroupId, activatedAt; new reason values activated, parent_cancelled, oco_cancelled
  • createConditionalOrder deprecated for TP/SL: Use parent TP/SL on order placement or sdk.positions.attachPositionTpSl for existing positions
No breaking changes. Read full release notes →

Monaco Protocol SDK v0.7.8

New Features

  • sdk.vault.retryWithdrawal(withdrawalIndex): New method to re-submit a withdrawal whose on-chain transaction failed or was dropped, without allocating a new withdrawal slot

Changes

  • sdk.vault.withdraw() return type: Now returns WithdrawResult (extends TransactionResult with withdrawalIndex: number). Store withdrawalIndex — required to retry a failed submission
  • position_id rejected in position request bodies: The API gateway now rejects position_id in the JSON body of POST /positions/{id}/close, POST /positions/{id}/add-margin, and POST /positions/{id}/reduce-margin. The field is path-only. SDK callers are unaffected — raw HTTP callers must remove it from the request body
Read full release notes →

Monaco Protocol SDK v0.7.2

New Features

  • Vault deposits upgraded: Vault contract now embeds the application’s client_id in on-chain deposit calls. sdk.vault.deposit() unchanged — no migration required
  • ApplicationConfigResponse.clientId: getApplicationConfig() response now includes the application client ID
  • Margin transfer balance events: UserBalanceEvent.data.reason now includes "margin_transfer_in" and "margin_transfer_out"
  • TransferCollateralResponse expanded: New new_total_collateral_value and new_withdrawable_collateral fields — update collateral UI without a follow-up fetch
  • useUserBalances (React): Spot events preserve margin-collateral component; margin-transfer events replace the full total
No breaking changes. Read full release notes →

Monaco Protocol SDK v0.7.0

New Features

  • Perpetuals: First-class perp surface — sdk.marginAccounts and sdk.positions modules, perp helpers on sdk.market, perp options (marginAccountId, positionSide, leverage, reduceOnly) on placeLimitOrder / placeMarketOrder
  • Conditional orders: Standalone sdk.trading.createConditionalOrder plus attachPositionTpSl for TP/SL on existing positions
  • WebSocket: New conditional_order_update event streams TP/SL lifecycle — subscribe via sdk.ws.conditionalOrders(handler, tradingPairId?)
  • React: New usePositions hook; useMarket and useTrade extended with perp helpers

Breaking Changes

  • Pagination standardized to page / page_size: limit, offset, and skip removed across all paginated endpoints; total_count renamed to total
  • Trading pair fields renamed: Order.trading_pair → .trading_pair_id, ProfileOrder.trading_pair → .symbol, WebSocket order events tradingPair → symbol
  • Flattened response shapes: data wrapper removed from trades and market responses (e.g., getPaginatedTradingPairs now returns response.trading_pairs directly)
  • Removed success field from batch order, trading-pair, and candlestick response types — use HTTP status or total_failed === 0
  • Close-position requests: position_id only accepted in URL path, not body
Read full release notes →

Monaco Protocol SDK v0.6.3

Bug Fixes

  • MCP server: Added resetSDK for test isolation. No user-facing changes
Read full release notes →

Monaco Protocol SDK v0.6.2

Bug Fixes

  • revokeToken() parameter removed: sdk.auth.revokeToken() no longer accepts a refreshToken argument. The server identifies the session from the access token in the Authorization header — passing a body was causing a silent 400 error, meaning sdk.logout() was silently failing. Remove the argument from any direct revokeToken() calls; sdk.logout() requires no changes
  • MCP server: Network config is now passed explicitly to registerTools, fixing flaky CI tests from parallel process.env mutations. No user-facing changes
Read full release notes →

Monaco Protocol SDK v0.6.1

Simulate Fees Improvements

Improves fee simulation to support the 100% balance slider and accurate fee estimation for market orders. New Features:
  • max_quantity: Response now includes the maximum quantity the authenticated user can afford at the given price, after fees — use this directly for the 100% slider instead of computing balance / price
  • order_type=MARKET: Pass order_type: "MARKET" to get a slippage-adjusted buy_order_lock_amount that accounts for worst-case execution cost
  • slippage_tolerance_bps: Configurable slippage tolerance (0–1000 bps, default 500). Only valid with order_type: "MARKET"
Updated Types in @0xmonaco/types: SimulateFeeParams (new order_type, slippage_tolerance_bps), SimulateFeeResponse (new max_quantity, max_quantity_raw, slippage_tolerance_bps) No breaking changes. Read full release notes →

Monaco Protocol SDK v0.6.0

Portfolio Analytics API

This release adds portfolio analytics to the TypeScript SDK — aggregate trading statistics and time-series chart data for the authenticated user. New Features:
  • getPortfolioStats(period): Fetch aggregate stats (PnL, volume, fees, win/loss ratio, max drawdown, total equity) scoped to 24h, 7d, 30d, or all
  • getPortfolioChart(period, metric): Fetch bucketed time-series data for volume or pnl over a given period
  • getUserTrades(params?): Fetch paginated trade history with optional filtering by trading pair
  • Fee Rates on Profile: getProfile() now returns taker_fee_bps and maker_fee_bps on UserProfile
New Types in @0xmonaco/types: PortfolioPeriod, PortfolioMetric, PortfolioStats, PortfolioChartPoint, PortfolioChartResponse, UserTrade, GetUserTradesParams, GetUserTradesResponse No breaking changes.

Monaco Protocol SDK v0.5.9

Movements API Cleanup & Order Status Update

This release removes the unsupported source filter from movements APIs and adds SETTLED_ON_CHAIN as a terminal order status. Breaking Changes:
  • source Removed from Movements APIs: getPaginatedUserMovements() and useUserMovements() no longer accept source. It was never supported by the API and caused a 400 error if passed. Remove source from any calls that use it — other filters (entry_type, transaction_type, asset_id) are unaffected
New Features:
  • SETTLED_ON_CHAIN Order Status: Added as a terminal OrderStatus value representing a filled order fully settled on-chain. Full lifecycle: SUBMITTED → PARTIALLY_FILLED → FILLED → SETTLED → SETTLED_ON_CHAIN
Read full release notes →

Monaco Protocol SDK v0.5.8

Profile Endpoint Simplification & Market Order Cleanup

This release removes extraneous fields from the /me profile endpoint and eliminates timeInForce from market orders. Breaking Changes:
  • /me Response Simplified: GET /api/v1/accounts/me now returns only core profile metadata. balances, recent_movements, and recent_orders are no longer included — use /balances, /movements, and /orders directly
  • timeInForce Removed from Market Orders: The API rejects market orders that include time_in_force. Removed from @0xmonaco/types, @0xmonaco/core, and @0xmonaco/react. Limit orders are unaffected
React SDK (@0xmonaco/react):
  • useProfile() now exposes balances, movements, orders, and loading as top-level state, populated in parallel by fetchProfile()
  • New getPaginatedOrders action on useProfile() for manual order pagination
Read full release notes →

Monaco Protocol SDK v0.5.7

Batch Orders, MCP Server & Network Configuration Improvements

This release introduces batch order creation and replacement for high-frequency trading workflows, a fully rebuilt MCP server for AI assistant integrations, removes the manual WebSocket URL configuration requirement, and tightens the network parameter to prevent misconfiguration. New Features:
  • Batch Create Orders: Submit multiple orders in a single API call with sdk.trading.batchCreate() / useTrade().batchCreate()
  • Batch Replace Orders: Replace multiple orders atomically with sdk.trading.batchReplace() / useTrade().batchReplace()
  • Per-Order Results: Each order is processed independently — failures don’t block the rest; detailed outcome per order including match result and error details
  • MCP Server: Rebuilt from the ground up with 26 tools across 9 modules. Works in public-only mode with zero configuration — add your wallet and client ID to unlock full trading from any MCP-compatible AI assistant
  • Movements Filters: getPaginatedUserMovements() and useUserMovements() now support server-side filtering by entry_type, transaction_type, and asset_id
Breaking Changes:
  • wsUrl Removed: The wsUrl option has been removed from SDKConfig and MonacoProvider. WebSocket URL is now automatically resolved from the network parameter
  • Network Type Tightened: Only preset network names are accepted. Passing a custom URL string now throws InvalidConfigError
  • useUserMovements Signature Changed: Now accepts an options object — useUserMovements({ maxMovements: 50 }) instead of useUserMovements(50)
Read full release notes →

Monaco Protocol SDK v0.5.6

Real-Time Features & API Improvements

This release introduces real-time balance tracking via WebSocket, refines the batch cancel API with two dedicated methods, and improves vault error handling. New Features:
  • Real-Time Balance Updates: New sdk.ws.balances() WebSocket channel for live balance updates triggered by deposits, withdrawals, trades, and order locks
  • useUserBalances Hook: React hook combining REST + WebSocket for real-time balance tracking with manual refresh capability
  • Batch Cancel Split: batchCancelOrders() split into batchCancel(orderIds) and batchCancelAll(tradingPairId?) for clearer API and optional trading pair filtering
  • 14 React Hooks: Hook count corrected - Monaco React SDK now has 14 hooks including the new useUserBalances
Breaking Changes:
  • viem Peer Dependency: Moved to peer dependency (^2.45.2) - consumers must install viem directly to prevent type duplication issues
  • batchCancelOrders Removed: Replaced with batchCancel() and batchCancelAll() methods for clearer intent
Bug Fixes:
  • Improved vault error handling with clear InvalidConfigError messages when no wallet account is connected
Read full release notes →

Monaco Protocol SDK v0.5.5

Batch Cancel Orders & Enhanced React Hooks

This release introduces batch order cancellation, new React hooks for real-time data, and simplifies order statuses by removing deprecated intermediate states. New Features:
  • Batch Cancel Orders: Cancel multiple orders in a single API call with sdk.trading.batchCancelOrders()
  • Cancel All Orders: Use { cancelAll: true } to cancel all active orders instantly
  • useUserOrders Hook: Real-time order tracking via WebSocket with automatic state management
  • useOHLCV Hook: Live candlestick data for charting with WebSocket updates
Breaking Changes:
  • Order Status Simplification: PENDING and ACKNOWLEDGED statuses removed - orders now transition directly to SUBMITTED
  • vault.getBalance() Deprecated: This method no longer functions - use profile.getUserBalanceByAssetId() or profile.getUserBalances() instead
  • MarketMetadata Fields Nullable: last_price, high_24h, low_24h, volume_24h, and other fields are now nullable for markets without trade history
  • TypeScript Type Updates: Field name changes in GetUserBalancesResponse, AccountBalance, and LedgerMovement types (see full release notes for details)
Read full release notes →

Monaco Protocol SDK v0.5.4

Real-Time User Movements & Trade Feed Improvements

This release introduces real-time user movement events via WebSocket and refactors the useTradeFeed React hook for better performance. New Features:
  • Movements WebSocket Channel: Subscribe to authenticated, real-time ledger movement events
  • New SDK Method: sdk.ws.movements(handler) for subscribing to balance change events
  • React Hook: useUserMovements() with automatic deduplication and configurable history limit
  • Shared Trade Feed: useTradeFeed now shares WebSocket subscriptions across components
Breaking Changes:
  • useTradeFeed no longer accepts maxTrades parameter (fixed at 50 trades)
  • useTradeFeed no longer returns clearError function
Bug Fixes:
  • Fixed self-referential import in useOrderbook hook
  • Fixed circular dependency between provider and hooks modules
  • WebSocket now supports multiple handlers per channel
Read full release notes →

Monaco Protocol SDK v0.5.3

Candlestick API Refactor & Market Metadata

This release refactors the candlestick API to use trading pair UUIDs and introduces TradingView-compliant query patterns. Breaking Changes:
  • getCandlesticks now requires tradingPairId (UUID) instead of symbol
  • Method signature changed from (symbol, interval, startTime, endTime) to (tradingPairId, interval, params?)
New Features:
  • Optional startTime, endTime, and limit parameters via GetCandlesticksParams
  • Support for backward pagination (endTime + limit) — TradingView’s countBack pattern
  • Support for forward pagination (startTime + limit)
  • Limit validation: max 500 bars per request, server defaults to 350
  • New market metadata endpoint with 24hr stats and latest price
  • Auto-connect flag for authenticated WebSocket channels
  • Vault API now uses assetId (UUID) instead of token addresses
Read full release notes →

Monaco Protocol v0.5.2

gRPC API & Testnet Faucet

This release introduces the Monaco gRPC API for high-performance trading and a new testnet faucet endpoint. gRPC API:
  • Full trading support: create, cancel, and replace orders via gRPC
  • Real-time market data: orderbook snapshots, recent trades, and OHLCV candles
  • Account management: profile, balances, movements, and sub-account limits
  • Binary Protocol Buffers serialization for reduced payload size
  • HTTP/2 multiplexing for concurrent requests
Testnet Faucet:
  • New POST /api/v1/faucet/mint endpoint for authenticated users
  • Mints all testnet tokens (BTC, ETH, SOL, SEI, USDC, and stock tokens)
  • Daily rate limit configurable via feature flags
Read full release notes →

Monaco Protocol v0.5.1

Architecture Evolution & Microservices

This release marks a significant evolution from a monolithic API to a distributed microservices architecture designed for high-frequency trading. Microservices Architecture:
  • api-gateway-rust: REST API for trading, accounts, market data (HTTP/1.1, Port 8080)
  • ws-api: Real-time orderbook & trade streaming (WebSocket, Port 8080)
  • grpc-api: High-performance trading API (gRPC/HTTP2, Port 50051)
  • matching-engine: Order matching & trade execution
Path-Based Routing:
  • REST API: https://{host}/
  • WebSocket: wss://{host}/ws
  • gRPC: https://{host} over HTTP/2 with gRPC method paths
Read full release notes →

Monaco Protocol SDK v0.5.0

WebSocket API Refresh & Trades Endpoint

This release introduces a new Trades REST API, real-time trades WebSocket channel, and updates the WebSocket SDK to use a cleaner functional pattern. New Features:
  • Trades REST API: GET /api/v1/trades/{trading_pair_id} for fetching trade history
  • Trades WebSocket channel: trades:{trading_pair_id} for real-time trade events
  • Balance by asset endpoint now properly documented
SDK Updates:
  • WebSocket API now uses functional pattern: sdk.ws.orders(), sdk.ws.orderbook(), sdk.ws.ohlcv(), sdk.ws.trades()
  • Subscription methods return unsubscribe functions for cleaner cleanup
  • wsUrl is now a required parameter in SDK initialization
Documentation:
  • Complete WebSocket documentation rewrite with updated examples
  • New Trades API reference documentation
  • Fixed balance endpoint path parameter (asset_id instead of token)
Read full release notes →

Monaco Protocol SDK v0.4.1

SDK Updates & Infrastructure Improvements

This release includes SDK dependency updates and backend infrastructure improvements, migrating the indexer from deprecated ethers.rs to alloy, along with critical bug fixes. SDK Updates:
  • TypeScript 5.9+ with improved type safety and IntelliSense
  • viem 2.31+ for enhanced blockchain interactions
  • Optimized TypeScript configuration recommendations
Infrastructure Improvements:
  • Alloy migration replacing deprecated ethers.rs library
  • WebSocket-based vault event listening (works with any standard EVM provider)
  • Repository pattern implementation for cleaner data access
  • Enhanced gap detection and backfilling logic
  • Fixed withdrawal event signature bug
  • Fixed critical whitelist app bug
Technical Enhancements:
  • New repositories: ApplicationRepository, BlockchainEventsRepository, ProcessedBlocksRepository
  • Improved RPC provider compatibility (no special filters required)
  • Enhanced test coverage with new integration and unit tests
Read full release notes →

Monaco Protocol SDK v0.4.0

Custom RPC Configuration & Network Flexibility

This release introduces configurable RPC endpoints and custom network support, giving developers full control over their infrastructure. Key Features:
  • Required seiRpcUrl parameter for SDK initialization
  • Custom RPC provider configuration to avoid rate limits
  • Custom network configuration support
  • Enhanced infrastructure control for production apps
Breaking Changes:
  • seiRpcUrl is now required when initializing the SDK
  • Affects all TypeScript SDK and React MonacoProvider usage
Read full release notes →

Monaco Protocol SDK v0.3.1

Balance Management Enhancements

This release adds new SDK methods for querying user token balances with pagination and individual token lookups. Key Features:
  • Paginated balance listing with getUserBalances()
  • Individual asset balance queries with getUserBalanceByAssetId()
  • Enhanced balance details (available, locked, total)
Read full release notes →

Monaco Protocol v0.3.0

Orderbooks and Flat Fees

This release introduces major improvements to orderbook functionality, fee management, and order lifecycle handling with real-time orderbook snapshots, flat fee support, and comprehensive time-in-force options. Key Features:
  • Real-time orderbook snapshots via WebSocket with magnitude grouping
  • Flat fee support and dedicated fee API endpoints
  • Full Time-in-Force implementation (GTC, IOC, FOK)
  • Average fill price tracking in order responses
  • Enhanced order validation and rejection events
  • Consistent uppercase enum formatting across entire API
Read full release notes →

Monaco Protocol SDK v0.2.0

Trading Enhancements & Real-time Data

This release introduces significant trading improvements including market order slippage protection, advanced limit order features, and real-time WebSocket data streaming. Key Features:
  • WebSocket real-time order events with 8 event types
  • WebSocket real-time OHLCV candlestick data (6 timeframes: 1m, 5m, 15m, 1h, 4h, 1d)
  • Automatic reconnection and connection health monitoring
  • Slippage tolerance for market orders (0.1% to 10%)
  • Good Till Cancel (GTC) with 90-day default expiration for limit orders
  • Custom expiration dates for limit orders
  • Enhanced order response with slippage metrics
Read full release notes →

Monaco Protocol SDK v0.1.6

Multi-Vault Architecture Support

The SDK now supports multiple vault contracts to align with the new backend architecture. Each client ID (app) now uses its own dedicated contract, providing better isolation and scalability. Key Features:
  • Multiple vault contract support
  • Enhanced contract isolation
  • New React package
Read full release notes →

Monaco Protocol SDK v0.1.5

General Release Update

Stable release with improved reliability and performance optimizations across all packages. Improvements:
  • Enhanced stability across the SDK
  • Performance optimizations
  • Bug fixes and reliability improvements
Read full release notes →

Monaco Protocol SDK v0.1.1

Initial Release

The first stable release of the Monaco Protocol SDK, providing developers with essential tools to integrate with the Monaco Protocol CLOB. Core Features:
  • Complete TypeScript SDK
  • Authentication system
  • Trading operations
  • React integration
  • Comprehensive API coverage
Read full release notes →