> ## Documentation Index
> Fetch the complete documentation index at: https://docs.0xmonaco.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

# Release Notes

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

***

## [Monaco Protocol SDK v1.0.85](/releases/v1.0.85)

*Latest Stable Release*

### Added

* **`APIError.serverCode`** — the REST error envelope's stable `code`
* **More margin figures on account frames** — optional `initialMarginRequired`, `withdrawableCollateral` and `realizedPnl` on `sdk.ws.account` frames and snapshot rows
* **`origin` on order snapshot rows** — and on every order event, `USER` included
* **React `login` takes options** — `connectWebSocket`

### Changed

* **Money on the USDC grid** — 6-decimal amounts; transfers, allocations and `riskBucketCollateral` past 6 decimals are refused; the order price ceiling is 100,000,000 per unit
* **Margin through the mark** — a margin limit order resting through the mark is charged initial margin at the mark plus its loss against the mark
* **Perp price band at entry** — an aggressive-side perp limit past the band is rejected with `INVALID_ORDER`; a resting order a mark move leaves past it is cancelled with `PRICE_BAND`

### Fixed

* **`ACCOUNT_DEACTIVATED`** — a deactivated account is refused on sign-in, on signed requests and on the WebSocket, where the SDK stops reconnecting
* **Positions snapshot serves live state** — and a snapshot row wins an equal-`version` tie on every field
* **Order preview** — a no-fill `IOC` reports `INSUFFICIENT_LIQUIDITY`, and a close that retires an isolated risk bucket reports it closed

## [Monaco Protocol SDK v1.0.83](/releases/v1.0.83)

### Breaking

* **TWAP slices leave the order listing** — `getPaginatedOrders`, `iterateOrders` and `useUserOrders` no longer return child orders that carry `parentOrderId`, and `total` no longer counts them; read them with `sdk.trading.listTwapOrderSlices`

### Added

* **Open interest on candles** — optional, nullable `openInterestBase` and `openInterestNotional` on `getCandlesticks`, `sdk.ws.ohlcv` and `useOHLCV` candles: one-sided LONG, sampled at or before the candle end

### Changed

* **One full-close take-profit and stop-loss per position** — position-attached and entry-order full-close legs share the slot, and the most recently set leg wins

### Fixed

* **A busy session check is `503`, not `401`** — `SERVICE_UNAVAILABLE` / gRPC `UNAVAILABLE`, `Authentication temporarily unavailable; retry`; keep the session and retry
* **A replacement re-checks the TP/SL it carries** — a wrong-side trigger is refused with `INVALID_ORDER`, and a replacement that fills on arrival arms its legs
* **The first cross order's risk preview matches placement** — the risk bucket is modeled empty, drawing what the order needs
* **The mainnet preset reaches the production REST API** — `https://apimonaco.xyz`

## [Monaco Protocol SDK v1.0.82](/releases/v1.0.82)

### Added

* **What placed an order: `origin`** — `USER`, `LIQUIDATION`, `ADL`, `COPY` or `SYSTEM` on `getOrder` and `getPaginatedOrders` rows and `useUserOrders`; liquidation and ADL closes report it on order events too
* **Server-confirmed WebSocket subscriptions** — `sdk.ws.getSubscriptionState` and `sdk.ws.onSubscriptionChange`
* **Batch close on several pairs, and on one side** — `batchCloseAllPositions` takes `tradingPairIds` (1 to 256) and `side`
* **A bounded count on user trades** — `getUserTrades` returns `total`, `totalPages` and `totalCapped` on an unfiltered walk; absent means unknown
* **List a TWAP order's slices** — `sdk.trading.listTwapOrderSlices` (`GET /api/v1/orders/twap/{twapOrderId}/slices`)
* **`estimatedNextFundingRate` on screener rows** — the in-progress window's estimate, `null` for spot

### Changed

* **`batchCloseAllPositions` rejects unknown keys** — a REST body with one gets `400` and closes nothing; the SDK throws a `ValidationError` before sending
* **Open interest is documented as gross** — descriptions only

### Deprecated

* **PitPass names for TraderCodes** — use `sdk.tradercodes` and `/api/v1/tradercodes/...`; `sdk.pitpass` and `/api/v1/pitpass/...` stay as aliases

### Fixed

* **Order fee fields are amounts** — quote amounts summed over fills, not basis points; taker fees equal the order's trades' fees
* **The next funding time is always ahead** — no more countdown stuck at 00:00:00
* **`AUTH_REQUIRED` channels recover after login** — re-sent once the connection authenticates
* **An agent's orders carry the owner's wallet** — for self-trade prevention and TraderCodes referral rewards

## [Monaco Protocol SDK v1.0.81](/releases/v1.0.81)

### Breaking

* **One margin mode per market and margin account** — an order that is not reduce-only into the bucket that is not holding the pair is rejected with `400` / `INVALID_ARGUMENT` and `MARGIN_MODE_CONFLICT`; reduce-only orders always pass, and copy skips report `MARGIN_MODE_CONFLICT`

### Added

* **`rejectCode` on order-risk previews** — the structured code placement would return, beside `rejectReason`
* **Lifetime `fundingPaid` on open positions** — on `OPEN` and `LIQUIDATING` rows and on `positions` frames; absent, never zero, when it cannot be served whole
* **A subscribe-time snapshot on `ohlcv`** — `sdk.ws.ohlcv` takes an optional `onSnapshot` with the newest 350 candles, oldest first; upsert live candles by `T`

### Changed

* **`1013` when events are lost before the WebSocket server** — every subscribed connection on that server reconnects through `onResync({ slowClient: true })`

### Fixed

* **Spot win rate counts the buy-side fee** — spot closes are classified net of both fees
* **Margin balance frames always name a token** — no more empty `tokenAddress` after a perp fill
* **TraderCodes naming** — documentation only

## [Monaco Protocol SDK v1.0.80](/releases/v1.0.80)

### Added

* **`lifetimeTotalsStatus` on positions** — `COMPLETE`, `PENDING` (retry after a short backoff) or `INCOMPLETE` says whether a position's lifetime totals can be trusted; never read an absent total as zero, and an open position whose history does not reconcile no longer returns `cumFees`
* **The margin ladder on `instruments` frames** — perp frames carry `riskTiers`, and any ladder edit sends a `config_change` naming `risk_tiers`
* **A subscribe-time snapshot on `trades`** — `sdk.ws.trades` takes an optional `onSnapshot` with the newest 50 trades, newest first; dedupe on `tradeId`
* **`maxRemovableCollateral` on risk-bucket rows** — the largest removal when an isolated bucket's one position may give back margin down to its initial margin
* **Optional `serde` feature in the Rust gRPC SDK** — REST-shaped JSON derives on every generated message, off by default

### Changed

* **Closed and liquidated positions list newest-closed first** — `status=CLOSED` and `status=LIQUIDATED` order by `(closedAt, positionId)` descending; a page token issued before this release is rejected with `400`
* **Flat margin accounts get `account` frames** — a collateral move on an account with no position and no resting order sends one `account_update`

### Fixed

* **Market orders report no time in force** — `timeInForce` is `null` on MARKET order events and absent on order reads
* **Win rate counts fees** — perp closes are classified as winners or losers net of fees
* **Closed risk buckets** — cross transfers into a closed bucket move the money, and isolated previews after a close work like a first isolated order
* **Insufficient-balance errors show asset units** — `Available` and `Required` are in the asset's units, and an over-balance transfer returns the insufficient-balance error
* **A risk bucket leaves bad debt flat** — once the insurance fund covers the shortfall, an isolated bucket closes and a cross bucket returns to normal, empty
* **Referral and BuilderCodes reads are per client application** — documentation only

## [Monaco Protocol SDK v1.0.77](/releases/v1.0.77)

### Breaking

* **Reduce-only edits take the order's total** — `replaceOrder` and `batchReplace` read a margin reduce-only order's `quantity` as its new total and rest `total - filled`; a total at or below the filled amount is refused, and `updatedFields.quantity` reports the total

### Added

* **Index, funding and open-interest notional on `marketStatsAll` rows** — perp rows carry `indexPrice`, `fundingRate` (estimated next window), `premium` and `openInterestNotional`, which is omitted until open interest and a price are known

### Changed

* **The `instruments` snapshot includes inactive markets** — delisted markets arrive flagged `isActive: false`; filter on `isActive` for the tradable set

### Fixed

* **TWAP taker slices send every taker frame** — trades, order book, candles, balances and positions update on a slice fill, and passive children update the order book

## [Monaco Protocol SDK v1.0.76](/releases/v1.0.76)

### Breaking

* **Cursor pagination on four more listings** — `page` is removed from public trades, pending withdrawals, managed-market assignments and BuilderCodes payouts; walk with `pageToken` / `nextPageToken`, and `pageSize` accepts 1 to 1000. Payouts now order by `(createdAt, id)`, which is not chronological inside one persistor batch
* **`getTrades` returns an envelope** — `sdk.trades.getTrades` resolves to `{ trades, nextPageToken }`, and the MCP `get_trades` tool takes `pageToken`
* **`maxPositionNotional` is a USD cap** — each risk tier's `maxPositionNotional` is its USD notional cap; `maxPositionSize` is deprecated and no longer set

### Added

* **Market position and open-interest caps** — optional USD caps checked at admission at the mark, refused with `position cap exceeded` or `open interest cap reached`
* **More `market_stats` fields** — `openInterestHeadroom`, `marketStatus`, `fundingIntervalSeconds` and `lastFundingTime`; perp open interest is omitted until known
* **Asset identity on `instruments` frames** — asset IDs, tokens, names, decimals and perp `minLeverage`
* **Edited orders report their carried fill** — the replacement's `OrderPlaced` carries `filledQuantity` and `averageFillPrice`, and maker fills report the order's cumulative average
* **Per-market perp price band** — a market can set its own band (100 to 2,500 bps) in place of the 1,000 / 1,200 bps defaults

### Changed

* **BuilderCodes reads and claims are not switch-gated** — `buildercodesEnabled` can be `false` on a successful config read, and `setBuilderFee` is refused only while both fee switches are off
* **Cancels can return `OVERLOADED`** — a retryable `503` when the engine's work budget is full
* **Direct vault deposits need the exact payload** — a JSON-shaped `applicationData` that is not a canonical payload is credited to the default application's spot wallet

### Fixed

* **`useUserOrders` and edited orders** — the replacement row shows its carried fill

## [Monaco Protocol SDK v1.0.75](/releases/v1.0.75)

### Breaking

* **Cursor pagination everywhere** — `page` is removed from the positions, position-history, margin-account, movements, balances, funding and application listings; walk with `pageToken` / `nextPageToken`, `pageSize` accepts 1 to 1000, and bounded counts carry `totalCapped`
* **Public application reporting endpoints** — the application listings and stats take a required `clientId` instead of `x-server-key`, `setServerKey` is removed, and the five share a per-IP limit of 60 requests per minute
* **`GET /api/v1/orders` lists book orders only** — `getPaginatedOrders` returns limit and market orders; `ListedOrderType`, `Order.twap` and `Order.conditional` are removed; use `listTwapOrders` and `listConditionalOrders`
* **Paged PitPass downline earnings** — `getMyReferralDownline` pages `earningsBySource` (100 rows by default) and adds `summary.byLevel`; `level1Earned` to `level3Earned` are deprecated
* **New terminal reasons** — `PRICE_BAND` and `SLIPPAGE_TOLERANCE`

### Added

* **Position valuation frames** — `sdk.ws.positions(..., onValuation)` delivers periodic `position_valuation` frames
* **`openedAt` on positions** — REST, gRPC and WebSocket position reads report when the position opened
* **Sub-account collateral transfers** — `transferCollateralToSubAccount()` and `transferCollateralFromSubAccount()`, plus matching MCP tools
* **Isolated-only markets** — `TradingPair.isolatedOnly`; non-reduce-only cross orders are rejected with `CROSS_MARGIN_NOT_ALLOWED`
* **`connecting` status** — `onStatusChange` reports `"connecting"` and passes `WebSocketStatusInfo` (`attempt`, `hasConnected`, `nextRetryDelayMs`)

### Changed

* **Fee tiers count volume floors** — `getMyFeeTier` resolves the tier from the higher of weighted volume and any volume floor
* **Margin order admission prices the real cost** — mark-priced initial margin, fill loss against the mark and fees; closes are never refused for their loss
* **Collateral bounds round down** — `withdrawableCollateral` and `availableOrderCollateral` round down to the asset's decimals, and a transfer of the printed `withdrawableCollateral` is accepted

### Fixed

* **Account-to-account transfers**, **TP/SL on a just-opened position**, **adding margin right after open**, and **margin TWAP on a new risk bucket**

## [Monaco Protocol SDK v1.0.74](/releases/v1.0.74)

### Breaking

* **`accountState` becomes `riskBucketState`** — margin-account summaries carry optional `riskBucketState` on risk-bucket rows only (absent on the parent row, so any `state` filter excludes it), and `sdk.ws.liquidations` events expose `data.riskBucketState` in place of `data.accountState`

### Added

* **Margin sub-accounts** — `createMarginSubAccount()`, `transferMarginCollateral()`, `closeMarginSubAccount()` and `getMarginAccountPnl()`; risk-bucket transfer-in and preview take `marginAccountId`; `sdk.portfolio.getMargin({ marginAccountId, includeAccounts })` adds per-account and owner-rollup views; summaries gain `subaccountKey` / `marginAccountState` and order events carry `marginAccountId`
* **Secured delegated-agent policies** — AND-scoped margin account and market lists, `expiresAt` defaulting to 14 days (max 180), a closed action set with read-only `READ` keys, at most 10 active agents, an `ipAllowlist` for session creation, and `MANAGE_RISK_BUCKETS` for risk-bucket funding
* **Referral tree and upline** — `sdk.pitpass.getMyReferralTree()` pages one level of your tree down to L3, `getMyReferralPosition()` returns `upline`, and React adds `useMyReferralTree`
* **`AUTO_DELEVERAGING` cancel reason** — reduce-only orders cancelled because auto-deleveraging fully closed their position

### Changed

* **Withdrawable collateral counts realized PnL** — parent and risk-bucket bounds include settled realized PnL; a risk bucket's bound is no longer capped at its allocation, a flat cross risk bucket returns its allocation and PnL to the parent margin account, an order's unused auto-drawn collateral returns on cancel, and bad-debt risk buckets refuse transfers
* **BuilderCodes payouts credit `BUILDER_FEE`** — positive builder fees on enrolled taker fills accrue to the application payout wallet
* **Every engine write is rate limited** — settings writes, follow stops, position closes and reward transfers can return `429` / `RESOURCE_EXHAUSTED`
* **Ranked funding and terminal position frames** — non-zero funding and terminal closes carry the durable `version`; terminal frames keep historical size and exact status
* **Liquidation, ADL and bad debt** — forced closes ignore self-trade prevention, ADL starts when the book refuses the position and fills at a single pinned price, the venue switches to the Zero mode when the insurance fund is empty, bad debt drains from the fund with no manual write-off, and a risk bucket's loss stops at its allocation (live engine equity caps it; persisted portfolio reads do not)
* **Order margin and release** — margin limit orders price initial margin at their limit, automatic top-up returns only the unused draw on cancel, and margin release is floored at initial margin

### Fixed

* **Position reads return `503` on index-price outages** — `listPositions`, `getPosition` and `getPositionRisk` report transient index-price failures as retryable `503` / `UNAVAILABLE`
* **Funding publication resumes after epoch gaps** — epoch numbers may jump; skipped identifiers are not retroactive charges
* **Copy-position margin errors** — `addFollowPositionMargin` `409` covers only a stopping or stopped follow; `400` reasons match the copy position's state

## [Monaco Protocol SDK v1.0.73](/releases/v1.0.73)

### Added

* **Frontend builder-fee controls** — `sdk.buildercodes.setBuilderFee({ bps })` and cursor-paginated `listBuilderFeeChanges()`; `getConfig()` gains the stored fee, live charging gate/cap and rolling change allowance. Frontend payout-wallet sessions only, two real changes per rolling 24 hours; no-op updates consume none. Builder-fee payout credits remain inactive

### Changed

* **Conditional-order cardinality is bounded** — defaults of 10 active legs per position, 100 resident legs per user on an engine shard and 100,000 on that shard; entry-order cap refusals report `400`, but position attaches report generic `500` / `INTERNAL`. Full-close TP/SL replacements subtract the legs they supersede
* **Replacement field descriptions match the resulting order** — `updatedFields.quantity` is the replacement's total, not its resting remainder; price and quantity are reported even when omitted. Margin reduce-only quantities remain close sizes; no wire or runtime change

### Fixed

* **Withdrawal safeguards** — the vault itself is rejected as a destination before funds move, and delayed proof recording cannot regress an executed withdrawal to confirmed
* **Provisioning overload is retryable** — automatic parent margin account creation and cross risk bucket provisioning return `503` / `UNAVAILABLE` on sequencer overload instead of `400`
* **Market protection and valuation** — one last-fill vote per taking order in the five-order spot reference, including after restart; margin transfers bind registry USDC metadata and exact amounts without restating live lifecycle state; unsafe oracle valuations and position-margin changes are refused
* **Oracle delivery** — Pyth WebSocket delivery with polling when streams stop advancing, and deployment-configured managed SEDA feeds with an operator-selected Hyperliquid fallback when the upstream market is shut, not generic Pyth failure; cross-provider asset identity is not automatically verified
* **WebSocket reference completeness** — helper, wire-channel and reconnect lists now include the existing authenticated `copy` subscription; no new channel

## [Monaco Protocol SDK v1.0.72](/releases/v1.0.72)

### Added

* **Close copy positions and add copy-position margin** — `sdk.copyTrading.closeFollowPositions()` / `closeAllCopyPositions()` close copy positions at market and keep the follows (not a flatten guarantee; rate limited per account, `failedFollows` on close-all), and `addFollowPositionMargin()` moves margin from the follow's copy account into one copy position; also in `useCopyTrading()` and as MCP tools
* **TWAP updates carry a `version`** — optional on `twap_order_update` frames and `twap_orders` snapshot rows; compare within one `twapOrderId` with `>=`, snapshot wins ties

### Changed

* **Fee tiers are wallet-level** — the 14-day weighted volume sums a wallet's trades across every application and sets one tier everywhere; `GET /api/v1/fees/tier` fields are unchanged and report wallet-wide values from the next 00:00 UTC recompute
* **Position frames cover every touched position** — a fill frames every open position in each risk bucket it touched, and collateral transfers send `margin_added` / `margin_reduced` frames, ranked for direct isolated and copy-position adds, unranked for cross-risk-bucket allocation and copy follow/stop refreshes

### Fixed

* **Delegated sessions follow their grant** — an expired or inactive grant ends signed reads, refresh and WebSocket access as well as trading, and session `expiresAt` is bounded by the grant's expiry

## [Monaco Protocol SDK v1.0.70](/releases/v1.0.70)

### Breaking

* **Order and trade listings are cursor-only** — `getPaginatedOrders`, `listConditionalOrders`, `listTwapOrders` and `getUserTrades` no longer accept `page`; walk them with `pageToken` / `nextPageToken`, `pageSize` up to 1000. REST answers `400` to `page`, and user-trades responses lose `total` / `totalPages`
* **`riskBucketId` answers on margin order placement** — a risk bucket owned by another user or application returns `404` `Risk bucket not found`, and a bucket under a non-default `strategyKey` (copy trading risk buckets included) returns `400`; your own bucket's pair and account checks are unchanged

### Added

* **Copy trading** — `sdk.copyTrading` (thirteen methods over `CopyTradingService`, REST under `/api/v1/copy-trading`, including `previewFollow` and `listFollowLeaderPositions`), `sdk.ws.copy()` on the owner-private `copy` channel, `useCopyTrading()` / `useCopyEvents()` and the matching MCP tools; entry and growth are refused while the server-side switch is off. Copy risk bucket rows carry `copyFollowId` / `copyLeaderHandle`, and order events gain `origin` and `copyRelationshipId`
* **Order frames name their margin position** — `orders` events carry `positionId`, bound at admission when the margin account or risk bucket already holds a position in the pair; `OrderPlaced` adds `marginAccountId`, `leverage` and `reduceOnly`, and snapshot rows add `postOnly`, `expirationDate`, `triggerPrice` and `positionSide`

### Changed

* **Movement and funding-payment totals are bounded by pagination reach** — `total` saturates at `pageSize` × 10,000 rows and `totalPages` at 10,000 on `getPaginatedUserMovements()` and `listFundingPayments()`
* **Per-owner share for idempotency receipts** — each user and application holds at most 10,000 receipts not yet safe to evict (awaiting durable acknowledgement or persistence); past that, new keys get the retryable `ORDER_SUBMISSION_CAPACITY_EXCEEDED`

### Fixed

* **Delegated replacements check notional in base-asset units** — a quantity-less `ReplaceOrder` / `BatchReplaceOrders` inherits the remaining quantity, and an uncomputable notional is refused with `403` instead of a server error
* **Funding history lists only charged windows** — a window the risk engine refused is no longer listed

## [Monaco Protocol SDK v1.0.69](/releases/v1.0.69)

### Added

* **`orders` frames for TWAP children and the makers they fill** — a TWAP child sends the ordinary `OrderPlaced` / fill / `OrderCancelled` frames, and each resting order a TWAP slice fills receives its maker fill; every TWAP-child frame and `orders` snapshot row carries `parentOrderId`, which `CommonOrderEventData`, `OrderSnapshotItem` and the REST `Order` type now declare

### Changed

* **One versioned conditional-order update per transition** — `sdk.ws.conditionalOrders()` receives exactly one `conditional_order_update` per transition; `ConditionalOrderEventReason` gains `position_closed`, `reparented` and `expired`; every update carries `version` (merge with `incoming.version >= local.version`), and the triggered update carries `TRIGGERED` with `triggeredOrderId` and `triggeredAt`
* **Trade fees report the total fee paid** — `UserTrade.fee` from `sdk.profile.getUserTrades()` and `fee` on `orders` fill frames include the taker's builder fee, with optional `monacoFee` + `builderFee` components (and their `Raw` forms); no value changes while every application charges a 0 bps builder fee
* **BuilderCodes payouts carry their payout stream** — `BuildercodePayout.kind` is `REVENUE_SHARE` or `BUILDER_FEE`, `listPayouts({ kind })` filters to one stream (every credit today is `REVENUE_SHARE`), and `getPayoutSummary()` adds `byTokenAndKind` beside the unchanged `byToken`; the Rust gRPC `ListBuildercodePayoutsRequest` no longer derives `Copy`
* **Public whitelist submissions are closed** — `sdk.whitelist.submit` (`POST /api/v1/whitelist`; gRPC `WhitelistService.SubmitWhitelist`) returns `403` `FORBIDDEN` / `PERMISSION_DENIED`
* **Faucet quota is per wallet and application** — `sdk.faucet.mint` enforces the daily limit per wallet and application, and `sdk.faucet.getInfo` counts only the calling application's requests

### Fixed

* **Resting orders are cancelled when a risk bucket enters liquidation** — at the `PendingLiquidation` transition, with `terminalReason` `LIQUIDATION`, instead of waiting for the liquidation to reach the bucket
* **First-use automatically funded sells are sized from their limit** — topped up from the parent margin account when a `SELL` executes above its limit, instead of refusing affordable orders sized off the public best bid
* **Closed positions no longer count a partial-close fee twice** — optional `cumFees` is the whole-life fee total, deducted once from a closed position's `netRealizedPnl` and `realizedRoe`
* **Positions closed over many fills report lifetime figures** — reducing executions reconcile at a 0.000001 tolerance, so `exitPrice`, `netRealizedPnl`, `realizedRoe`, `fundingPaid` and `feesPaid` populate and `size` / `isolatedMargin` carry lifetime values

## [Monaco Protocol SDK v1.0.67](/releases/v1.0.67)

### 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](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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 `400`s. 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](/releases/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; a payout row's `appliedBps` is the realized whole-bps rate for that fill and may be lower. Field types and wire values are unchanged

## [Monaco Protocol SDK v1.0.58](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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](/sdk/typescript/websockets#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](/sdk/typescript/margin-accounts) and [Perps Collateral](/developers/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`](/sdk/react/hooks/use-instruments)

### 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](/releases/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](/trading-mechanics/twap-orders#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](/trading-mechanics/twap-orders#conditional-twap-price-triggered-activation)
* **`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](/developers/trade#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](/developers/account-balances#movements)
* **`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](/releases/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](/releases/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](/reference/websockets#instrument)
* **`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](/releases/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](/releases/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](/trading-mechanics/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](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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](/sdk/mcp-server#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](/releases/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](/sdk/typescript/websockets#positions-channel)
* **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](/sdk/react/hooks/use-tradercodes)

### 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](/releases/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](/developers/trade#post-only-orders)

## [Monaco Protocol SDK v1.0.36](/releases/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](/releases/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 `401`s; 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](/releases/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](/releases/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](/releases/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](/releases/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](/developers/market-maker-runbook#4-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](/releases/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](/releases/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](/sdk/typescript/portfolio) 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](/releases/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](/releases/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](/releases/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](/releases/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`)

***

## [Monaco Protocol SDK v0.8.22](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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](/releases/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 →](/releases/v0.8.14)

***

## [Monaco Protocol SDK v0.8.11](/releases/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 →](/releases/v0.8.11)

***

## [Monaco Protocol SDK v0.8.10](/releases/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 →](/releases/v0.8.10)

***

## [Monaco Protocol SDK v0.8.8](/releases/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 →](/releases/v0.8.8)

***

## [Monaco Protocol SDK v0.8.7](/releases/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 →](/releases/v0.8.7)

***

## [Monaco Protocol SDK v0.8.5](/releases/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 →](/releases/v0.8.5)

***

## [Monaco Protocol SDK v0.8.4](/releases/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 →](/releases/v0.8.4)

***

## [Monaco Protocol SDK v0.7.9](/releases/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 →](/releases/v0.7.9)

***

## [Monaco Protocol SDK v0.7.8](/releases/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 →](/releases/v0.7.8)

***

## [Monaco Protocol SDK v0.7.2](/releases/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 →](/releases/v0.7.2)

***

## [Monaco Protocol SDK v0.7.0](/releases/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 →](/releases/v0.7.0)

***

## [Monaco Protocol SDK v0.6.3](/releases/v0.6.3)

### Bug Fixes

* **MCP server**: Added `resetSDK` for test isolation. No user-facing changes

[Read full release notes →](/releases/v0.6.3)

***

## [Monaco Protocol SDK v0.6.2](/releases/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 →](/releases/v0.6.2)

***

## [Monaco Protocol SDK v0.6.1](/releases/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 →](/releases/v0.6.1)

***

## [Monaco Protocol SDK v0.6.0](/releases/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](/releases/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 →](/releases/v0.5.9)

***

## [Monaco Protocol SDK v0.5.8](/releases/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 →](/releases/v0.5.8)

***

## [Monaco Protocol SDK v0.5.7](/releases/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 →](/releases/v0.5.7)

***

## [Monaco Protocol SDK v0.5.6](/releases/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 →](/releases/v0.5.6)

***

## [Monaco Protocol SDK v0.5.5](/releases/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 →](/releases/v0.5.5)

***

## [Monaco Protocol SDK v0.5.4](/releases/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 →](/releases/v0.5.4)

***

## [Monaco Protocol SDK v0.5.3](/releases/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 →](/releases/v0.5.3)

***

## [Monaco Protocol v0.5.2](/releases/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 →](/releases/v0.5.2)

***

## [Monaco Protocol v0.5.1](/releases/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 →](/releases/v0.5.1)

***

## [Monaco Protocol SDK v0.5.0](/releases/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 →](/releases/v0.5.0)

***

## [Monaco Protocol SDK v0.4.1](/releases/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 →](/releases/v0.4.1)

***

## [Monaco Protocol SDK v0.4.0](/releases/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 →](/releases/v0.4.0)

***

## [Monaco Protocol SDK v0.3.1](/releases/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 →](/releases/v0.3.1)

***

## [Monaco Protocol v0.3.0](/releases/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 →](/releases/v0.3.0)

***

## [Monaco Protocol SDK v0.2.0](/releases/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 →](/releases/v0.2.0)

***

## [Monaco Protocol SDK v0.1.6](/releases/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 →](/releases/v0.1.6)

***

## [Monaco Protocol SDK v0.1.5](/releases/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 →](/releases/v0.1.5)

***

## [Monaco Protocol SDK v0.1.1](/releases/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 →](/releases/v0.1.1)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.