Release Notes
Stay up to date with the latest Monaco Protocol SDK releases, new features, and improvements.Monaco Protocol SDK v1.0.67
Latest Stable ReleaseAdded
- Screener open-interest averages —
sdk.market.getScreener()(GET /api/v1/market/screener; gRPCMarketService.GetScreener) rows gain nullableopenInterestNotionalAvg1h,openInterestNotionalAvg24handopenInterestNotionalAvg7d, andScreenerSnapshotPointgainsopenInterestNotionalAvg— 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 arenull; no backfill createdAton conditional-order frames —sdk.ws.conditionalOrders()live frames and snapshot rows carrycreatedAt, matching REST; optional onConditionalOrderEventDataonly 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
isolatedMarginonGET /api/v1/positionsand/positions/{position_id}and used forliquidationPricethere and on/positions/{position_id}/risk, excluding funds reserved for unfilled orders; live and persisted reads agree.riskBucketCollateralfunds 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(gRPCOrdersService.ListConditionalOrders/ListTwapOrders) walk(createdAt, id)newest-first bypageToken/nextPageToken,pageSizeup to 1000, withtotal/pagereturned as0. The server uses cursor mode only whenpageTokenis sent (raw clients start with""); the SDK’slistConditionalOrders/listTwapOrdersdefault to it. The deprecatedpagestill selects page-number mode
Fixed
- The liquidations channel has a baseline and reports recoveries —
ws.liquidationsreceives a subscribe-time snapshot of yourUNRESOLVEDrecords, and a recovered episode now sends a terminal alert withstatus: "COMPLETED"andaccountState: "Normal"instead of ending silently - Signing survives a skewed device clock — the SDK calibrates from each response’s
Dateheader, signs with the corrected time once the offset reaches 5 seconds, and retries once after a clock-correcting401; a skew-rejected WebSocket handshake re-signs up to 3 times, recovering once a REST response has calibrated the clock - Batch-cancel
cancelledAtis the engine’s stamp — millisecond precision, matching a later read of the order to the millisecond, instead of the API clock truncated to seconds - Funding premium samples are bounded before averaging — each sample is capped to the maximum funding rate and every sample in the window carries equal weight
- Higher WebSocket limits — 1,024 subscriptions per connection, burst 1,200, 50 authenticated connections per account per instance; a per-connection byte budget refuses bursts of very large frames with
MESSAGE_RATE_LIMIT
Monaco Protocol SDK v1.0.66
Added
- Trailing stops —
sdk.positions.attachPositionTpSl(POST /api/v1/positions/{position_id}/tp-sl; gRPCPositionsService.AttachPositionTpSl) acceptstrailingStop: TrailingStopLeg(trailBps10–2000,activationPrice?,quantity?/closePosition?,slippageToleranceBps?,expiresAt?), alone or alongsidetakeProfit/stopLoss, and returnstrailingStopOrderId;placeLimitOrder/placeMarketOrder(POST /api/v1/orders; gRPCOrdersService.CreateOrder) accept the same leg asoptions.trailingStopon margin entry orders that are not reduce-only, arming only on a full fill (the entry response carriestrailingStopOrderIdonly 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 returns409; no modify; never joinsoco.ConditionalOrderandconditionalOrdersframes gainconditionType: "TRAILING_STOP",trailBps,activationPrice,watermarkPriceandtrailArmedAt; live frames carryreason: "armed"andreason: "ratcheted"
Changed
- Position TP/SL attach is metered by the order-creation rate limit — each
attachPositionTpSlcall draws one item from the order-creation budget, whatever legs it carries; over budget it returns429RATE_LIMIT_EXCEEDED(gRPCRESOURCE_EXHAUSTED). Position closes stay unmetered; enforcement is warn-only until turned on for the environment Position.versionadvances only when the position changed — an unchanged position keeps itsversion, so a ranked liveposition_updatefor 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+1008orAUTH_FAILED, the client drops the rejected session, keeps handlers, reports"disconnected"and calls the newonReauthenticationRequired({ code, message, closeCode })once; a fresh session reconnects, resubscribes and firesonResync. Other abnormal closes keep the backoff reconnect onErrornames the channel a per-channel failure concerns —WebSocketErrorInfogainschannel?: stringonINVALID_SUBSCRIPTION,AUTH_REQUIRED,SUBSCRIPTION_LIMITandSNAPSHOT_UNAVAILABLEframes; connection-wide failures stay{ code, message }
Monaco Protocol SDK v1.0.65
Changed
- Page-number listing counts are bounded by their pagination reach —
sdk.profile.getUserTrades(GET /api/v1/accounts/trades; gRPCAccountsService.GetUserTrades/TradesService.ListUserTrades),sdk.positions.listPositions/listPositionHistory,sdk.marginAccounts.getMarginAccountMovements/getParentMarginAccountMovements, andsdk.trading.listConditionalOrders/listTwapOrdersnow bound the legacy page-numbertotal(andgetUserTrades’stotalPages) at each listing’s pagination reach: exact up topageSize× 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.pageabove 10,000 is rejected (page must be <= 10000). Unlike v1.0.64’stotalCappedon 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. OnlygetUserTradeshas a cursor (pageToken) mode — its SDK default, unchanged and still reporting no totals; the other six are page-number-only (page/pageSize), and theirtotalis the count they report, not deprecated
Fixed
- The
twapOrderschannel delivers a subscribe-time snapshot —sdk.ws.twapOrders(handler, tradingPairId?, onSnapshot)now receives a subscribe-time snapshot ahead of the live frames: oneTwapOrderEventDatarow per owned non-terminal (PENDING/ACTIVE) TWAP parent, optionally scoped to the subscription’s pair, each the shape of a livetwap_order_updatewithreason: "snapshot". Previously the server sent no TWAP snapshot, soonSnapshotwas inert and a baseline needed a REST read. The baseline reaches only subscriptions that pass anonSnapshothandler — a caller already passing one needs no code change, a caller subscribing assdk.ws.twapOrders(handler)should add one; an empty baseline arrives asonSnapshot([]), and an unavailable read surfacesSNAPSHOT_UNAVAILABLEwhile the subscription stays active - The
instrumentschannel carries base and quote asset icon URLs —sdk.ws.instruments(handler, tradingPairId?, onSnapshot)now surfacesbaseIconUrlandquoteIconUrl(string | null) on each parsedInstrumentEventData, on both the snapshot and live frames. The server sends each key on every market as an explicitnullwhen 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’stradingPairIdto the instrument row and render both icons without a REST read
Monaco Protocol SDK v1.0.64
Added
- Order history returns a bounded item count in both pagination modes —
sdk.trading.getPaginatedOrders(GET /api/v1/orders, authenticated; gRPCOrdersService.ListOrders) now populatestotalandtotalPagesin both page-number and cursor (pageToken) mode, bounded by a server ceiling of 10,000 so a large order history never triggers an unboundedCOUNT; cursor mode previously returned0.GetPaginatedOrdersResponsegainstotalCapped(boolean):truewhen more than the ceiling matched, sototal/totalPagesare a lower bound.totalandtotalPagesare no longer deprecated; onlypagestays0in cursor mode. The response schema is additive, but the count semantics change: cursor mode now populatestotal/totalPages(a count over full history, hot and archived merged) where it returned0, and a page-number caller above the ceiling in its recent-history window reads the capped10,000instead of the exact count of that window — checktotalCappedto tell an exact count from a saturated one
Monaco Protocol SDK v1.0.62
Breaking
- The
accountchannel snapshot makesAccountEventData.sequenceoptional —sdk.ws.account(handler, onSnapshot)now delivers a subscribe-time snapshot ahead of the live diffs: oneAccountEventDatarow 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 bothequityandfreeCollateral, with a zero maintenance requirement and margin ratio). Because a snapshot row is a live payload withsequenceomitted,AccountEventData.sequenceis nownumber | undefined— SOURCE-BREAKING for strict TypeScript: guardevent.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 permarginAccountIdand let any live frame supersede a snapshot row for the same account; rankupdatedAtonly between live frames, since a snapshot row’supdatedAtis 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 anonSnapshothandler (add one if you don’t already)
Added
- PitPass self-scoped referral reads —
sdk.pitpass.getMyReferralPosition()(GET /api/v1/pitpass/referrals/me) andsdk.pitpass.getMyReferralDownline()(GET /api/v1/pitpass/referrals/me/downline; gRPCTraderCodeService.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-walletdisplay.getMyReferralPositionreturns your direct (L1)referrer, all-timetotalEarned, and up to your 100 most recentReferralRewardEntryrows (totalEarnedstays the complete aggregate);getMyReferralDownlinereturnsearningsBySourceacross L1/L2/L3, your L1directRefereesroster (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; theearningsBySource/summaryearnings views stay unbounded), and an always-populated per-levelsummary.@0xmonaco/reactwraps both as theuseMyReferralPositionanduseMyReferralDownlinehooks - The verify response reports
isNewUserandreferralApplied—AuthState(fromsdk.auth.authenticate/sdk.auth.verifySignature,POST /api/v1/auth/verify) now carries two optional flags:isNewUseristruewhen this verify created the user, andreferralAppliedistruewhen a PitPass referral was recorded on it. Read both behind a presence check
Changed
- The
market_statschannels deliver a subscribe-time snapshot —sdk.ws.marketStats(pairId, handler, onSnapshot)andsdk.ws.marketStatsAll(handler, onSnapshot)(public, no auth) now receive a snapshot ahead of the live frames: a one-elementMarketStatsData[]for the per-market form, aMarketStatsSummary[](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’smarketslist. A per-market subscription whose market has not produced a frame yet receives aSNAPSHOT_UNAVAILABLEsignal rather than a fabricated row, and stays active. No SDK migration is needed, but the baseline reaches only subscriptions that pass anonSnapshothandler (add one if you don’t already) - Application listings bound
totalandtotalPagesto their pagination reach —client.applications.listApplicationOrders()andclient.applications.listApplicationMovements()(GET /api/v1/applications/ordersand/api/v1/applications/movements, server-key auth; gRPCApplicationsService.ListAppOrders/ListAppMovements) now captotalandtotalPagesat the listing’s reach: both stay exact up to 10,000 pages of the requestedpageSize(pageSize× 10,000 matching rows) and saturate there, so an application with more matching rows reads the cap withtotalPages=10000instead of its lifetime total. Loops that stop atpage >= totalPageskeep terminating; only code readingtotalas a lifetime count needs to treatpageSize× 10,000 as “at least this many”, and apageabove 10,000 is now rejected as invalid input
Monaco Protocol SDK v1.0.61
Breaking
riskTierscarries the full margin ladder —GET /api/v1/market/pairs/{trading_pair_id}/perp/config(sdk.market.getPerpMarketConfig; gRPCMarketService.GetPerpMarketConfig, public, no auth) now lists every notional bracket ascending bylowerBoundNotional, where it previously reported a single synthetic tier, so both the length and the meaning of the array change. EachRiskTiergainstierLevel(1-based,1is the lowest bracket),lowerBoundNotional("0"for tier 1),maxLeverage(optional — absent only on a single-tier market configured from explicit margin rates),initialMarginDeductionandmaintenanceMarginDeduction("0"for tier 1). The deductions are the continuity terms ofIM_floor(N) = N x initialMarginRatio - initialMarginDeductionandMM(N) = N x maintenanceMarginRatio - maintenanceMarginDeduction, and each bracket’s rates are marginal. A position’s bracket is the last tier whoselowerBoundNotionalis at or belowabs(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-levelmaxLeverage/initialMarginRatio/maintenanceMarginRatioare unchanged and remain the tier-1 values, andmaxPositionSize(with its deprecatedmaxPositionNotionalalias) stays a market-wide base-quantity cap repeated on every tier
Added
- Order-risk previews return the engine’s simulated fill —
sdk.marginAccounts.simulateOrderRisk,simulateParentMarginOrderRiskandsimulateRiskBucketOrderRisk(authenticated, owner-scoped; gRPCMarginAccountsService.SimulateOrderRisk/SimulateParentMarginOrderRisk/SimulateRiskBucketOrderRisk) now publish the match the engine already ran to admit the order.expectedMatchResult?: MatchResultis the same shapeCreateOrderResponse.matchResultcarries for a placement (theOrderread model does not carry it), from the same walk over the live book —totalFilled,remainingQuantity(the partial-fill indicator),averageFillPrice,executionPriceRange,status,actualSlippageBps,maxSlippageBps— andreferencePrice?: stringis the touch a MARKET preview was measured from. Both are present only whenacceptedis true and absent, never zeros, on every refused preview including the post-match maker-risk rejection;referencePriceis additionally MARKET-only, and a LIMIT or IOC preview is measured against its own limit price withactualSlippageBpsnullwhen every fill improved on it. The three requests gain an optionalslippageToleranceBps(MARKET only,0–1000), validated client-side by the newly exportedMarketSlippageToleranceBpsSchema, 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 applyactualSlippageBpsto a TP/SL trigger price rather than displaying the absolute average. The MCPsimulate_parent_margin_order_riskandsimulate_risk_bucket_order_risktools 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 ofsdk.trading.batchReplace(POST /api/v1/orders/batch-replace; gRPCOrdersService.ReplaceOrder/BatchReplaceOrders) rejectedquantityat or below the order’sfilledQuantityon every order, withNew 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 bothquantity: "2"andquantity: "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 underCANCEL_TAKER/CANCEL_BOTHor as aSKIPremainder, so read the new order’s status — and the replacement still inherits the original’s fill history. Nothing else changes: total semantics, thetotal - filledsubtraction and the must-exceed-filled rejection are unchanged for every other order, and the reduce-only position bound still rejects a close larger than the live position. No request or response shape changed, so clients on any SDK version receive it
Monaco Protocol SDK v1.0.60
Breaking
- A cross risk-bucket preview no longer takes
selectedTradingPairIds—sdk.marginAccounts.simulateRiskBucketOrderRisk(POST /api/v1/margin/risk-buckets/simulate-order-risk) drops the field formarginMode: "CROSS"; passing it throwsValidationErrornamingrequest.selectedTradingPairIdsbefore any request is made, and the type is now?: never, exactly asstrategyKeyalready 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 produced400s. A cross preview with no list, one omittingtradingPairId, 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 forISOLATED. 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 MCPsimulate_risk_bucket_order_risktool drops the field and its three refinements, declarespositionSide, 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 —
ConditionalOrdergainstriggeredOrder?: TriggeredOrderSummary(orderId,status,filledQuantity, optionalaverageFillPrice,totalTakerFees,filledAt,terminalReason), read from the triggered order’s own row at request time ongetConditionalOrder,listConditionalOrdersand underOrder.conditionalongetPaginatedOrdersrows. Present only oncestateisTRIGGEREDand the close can be read; absent on aFAILEDconditional, which recorded notriggeredOrderIdto join on — a close the engine placed and the book then rejected still persists as an order row carryingconditionalOrderId, while one refused before placement produces no order row at all — and absent on theconditional_ordersWebSocket frames.statusis copied verbatim from the close’s row, so switch on the fullOrderStatusunion with adefaultbranch:FILLEDis observable in the window before a filled leg reachesSETTLED, andCANCELLEDcovers aMARKET/IOCleg whose remainder was cut after a partial fill, so it can carry a nonzerofilledQuantity.Order.conditionalOrderIdis the reverse pointer ongetOrderandgetPaginatedOrders, 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’suseUserOrderscarries it through as a REST-only field. ComparetriggeredOrder.averageFillPricewithtriggerPriceas exact decimals rather than with===on the raw strings, and note the summary is a repository read whilegetOrderis 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.managedMarketsis a new authenticated namespace withlistAssignments,claimLeaseandrefreshQuotes, matched byGET /api/v1/managed-markets/assignments,POST /api/v1/managed-markets/{launchId}/leaseandPOST /api/v1/managed-markets/{launchId}/quotes, and by the gRPCManagedMarketsService. 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 with403, but only the launch-scopedclaimLeaseandrefreshQuotesrequire the session to match the configured maker user and application;listAssignmentsfilters by the caller and returns an empty page to a non-maker.ownerIdis a process fence, not authorization.generationandquoteRevisionareuint64fences carried as exact decimal strings in JSON — never round-trip them throughNumber— and an unknown quote outcome is reconciled throughclaimLeasebefore advancing the revision
Changed
- A
TRIGGEREDconditional row mirrors its close — ingetPaginatedOrders(GET /api/v1/orders), aCONDITIONALrow whose conditional has triggered now reports the order it fired into instead of a synthesizedFILLEDwithfilledQuantity: "0":quantityis the resolved leg size,filledQuantity/averageFillPrice/quoteVolumeand the terminal instants are the close’s own, andstatusis the close’s current status. Thestatusfilter applies the same rule. Aggregation needs two rules: the fee fields stay absent on that row (the close’s own row carries them, andconditional.triggeredOrder.totalTakerFeesrepeats the total), so fees sum cleanly over a page — but the close is listed again as its ownMARKETorLIMITrow, so sum quantities over those rows only - A triggered leg can be cancelled but not replaced —
sdk.trading.replaceOrder(PUT /api/v1/orders/{orderId}) andsdk.trading.batchReplacerefuse to replace theLIMITleg a TP/SL fired into — an order carryingconditionalOrderId— 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 anINVALID_ORDERrejection 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/screenerand the public price reads exclude a managed market until its durable runtime projection reaches live, including whenisActive=falseis passed explicitly. A pair with no launch row keeps its existing visibility
Monaco Protocol SDK v1.0.59
Breaking
BridgeRoutegainsdstEid,RouteKindgainsOft—@0xmonaco/contracts: theSWEEPER_FACTORY_ABItuple takes auint32 dstEidbetweencomposeandbridgeToken, so calldata encoded or decoded against the previous shape mismatches.0uses the chain-wide hub EID; a non-hub value builds a multi-hop lane.RouteKindgainsOft(4) for vanilla LayerZero OFT/OFTAdapter lanes, andSWEEPER_ABIgainsOftCannotUnwrap— anOftleg withunwrapNativeis refused. Nothing is deployed on-chain with the previous shape
Added
- The BuilderCodes API —
sdk.buildercodesgains five authenticated methods, matched by REST endpoints and the gRPCBuildercodeRewardsService:getConfig(),listPayouts({ page?, pageSize? }),getPayoutSummary(),getRewardsBalance(), andclaimBuildercodeRewards({ 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, whilegetRewardsBalancereads 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 above500plus a failure with no status at all — not only503: the command is admitted to the sequencer before its reply is sent and there is no idempotency key, so the SDK clearsretryableon all of them, a balance read does not settle it, and only the taggedOPERATIONS_BLOCKEDandOVERLOADEDcases 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 with400and the structured codeORDER_CAP_EXCEEDED, whose message names the binding scope. A book-state guard rather than a rate limit: fills and cancels free slots immediately,MARKETandIOC/FOKorders 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
versionon the position reads — the REST list and detail reads, the gRPCPositionmessage, the WebSocketpositionssnapshot and ranked liveposition_updateframes all carry an opaque producer-owned row revision; reconcile onpositionId + version. The risk read is the exception:GetPositionRiskResponsecarries noversion, 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 takemarkPrice,unrealizedPnl,liquidationPrice,leverage,maintenanceMarginRequiredandinitialMarginRequiredfrom the newer payload, since those six are re-derived on every read and carry no version.isolatedMarginis exempt too, despite being versioned: the snapshot overlays it from an independent live engine read, so take it from the newer payload as well.0is the unranked sentinel — treat it exactly as absent, and note a live-served REST or gRPC read reports0where an unranked WebSocket frame omits the field - Permissionless sweeps take a LayerZero fee —
@0xmonaco/contracts: the parameterlessSWEEPER_ABIsweep(address)and the factory’s permissionlesssweep(applicationId, user, token)are nowpayable, with excess refunded to the caller, so a fresh deposit address can deploy and sweep in one call.SWEEPER_ABIgainssweepWithRefund(token, feeRefundAddress), and direct Stargate routes join the permissionless path behind the owner-setpermissionlessBridgeFeeCapBps(default0, so the path is opt-in per chain), with new errors on two different contracts —BridgeFeeTooHighandUnexpectedBridgeDebitonSWEEPER_ABI,InvalidFeeCaponSWEEPER_FACTORY_ABI— so decode a revert against the matching ABI
Changed
- Contract-wallet (EIP-1271) login for BuilderCodes payout wallets —
sdk.auth.signChallengeasks the wallet to authorize the message rather than sign it with a private key: an EOA uses EIP-191personal_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(gRPCVerify) can answer a retryable503/UNAVAILABLEthat is not a verdict on the signature (a rejected signature, including a contract that reverts, is still401);signaturepublishes a bound of 8192 decoded bytes (16386 characters with0x), 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
positionschannel carries scope and margin requirements —sdk.ws.positions(...)now surfacesriskBucketId,marginMode,maintenanceMarginRequiredandinitialMarginRequiredon both snapshot rows and liveposition_updateframes, 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 missingmarginModeas unknown rather than defaulting it toCROSS.updatedAtnow 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 —versionremains the reconciliation key. A live-servedPosition.marginModeis 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/challengeand/verify(gRPCAuthService/ChallengeandVerify) are metered per client address at 60 requests/min sustained, burst 120 by default (overridable viaAUTH_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 — REST429carriesRetry-Afteranddetails.retryAfter, gRPCRESOURCE_EXHAUSTEDcarries agoogle.rpc.RetryInfodetail, and the hint is transport-specific — though it ships warn-only, admitting over-budget requests without a429untilauth_rate_limit_enforcedis turned on per environment
Fixed
payoutAddressaccepts anullwire shape —GetBuildercodesConfigResponseSchemapreviously 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 successfulgetConfig()in the released endpoint always carries an address, since it authorizes the caller as the payout wallet and returns403when none is set. The inferred type widens tostring | null | undefinedbuildercodesBudgetBpsandappliedBpsare different numbers —buildercodesBudgetBpsongetConfig()is the configured target, computed from the gross non-negative protocol take and capped by the post-floor remainder; a payout row’sappliedBpsis the realized whole-bps rate after that clamp and may be lower. Field types and wire values are unchanged
Monaco Protocol SDK v1.0.58
Changed
- Self-trade prevention dispatches by mode — an order meeting one of your own resting orders is resolved under the taker’s effective mode. Previously the engine silently skipped your resting orders and filled around them, rejecting the whole order only when the remaining third-party depth could not cover it; now the mode decides both halves. The platform default
CANCEL_MAKERcancels your resting order in full and lets the incoming one carry on into the book — unless the incoming order is a zero-fillMARKETorder 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_TAKERcancels the incoming remainder at the first self-touch and leaves the resting order;CANCEL_BOTHcancels both;SKIPtrades around your order, but cancels aGTCremainder rather than resting it into a surviving own order. The whole-orderSELF_TRADE_NOT_ALLOWEDrejection is removed from the match path, surviving only forFOK+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, notREJECTED— it persists as a real cancelled order and emitsOrderCancelledeven with zero fills, unlike an accepted zero-fillIOC/FOK, which staysREJECTED— for aFOKthat means insufficient qualifying liquidity, not an empty book: partial candidate depth is discarded when it cannot fill the whole quantity. (A plain zero-fillMARKETsubmit 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 —
OrderCancelledgains optionalstpCounterpartyOrderIdandOrderTerminalReasongainsSELF_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 onOrderPartiallyCancelledtoo, 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_movementframe (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 nouser_balancesleg, exactly as a user cancel of a margin order emits none
Added
- Per-order
selfTradePreventionMode— optional onplaceLimitOrder,placeMarketOrder,replaceOrder,batchCreateandbatchReplace(and theuseTradeequivalents), onPOST /api/v1/orders,PUT /api/v1/orders/{orderId}and both batch endpoints, and on the gRPCOrdersServicerequests. 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 withINVALID_SELF_TRADE_PREVENTION_MODE; the TypeScript SDK instead validates the whole array against its batch schema and throwsValidationErrorbefore sending, so no item executes. Also on the MCPplace_limit_order/replace_ordertools - Wallet self-trade-prevention default —
GET/PUT /api/v1/accounts/self-trade-prevention(sdk.profile.getSelfTradePreventionDefault/setSelfTradePreventionDefault; gRPCAccountsService.GetSelfTradePreventionDefault/SetSelfTradePreventionDefault) read and set one default per wallet family, returning{ mode, isCustom }whereisCustomisfalsewhile the platform defaultCANCEL_MAKERis 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 are400, and the setter returns503when 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 return403/PermissionDenied
Monaco Protocol SDK v1.0.57
Breaking
SweepParamsbinds a sweep to an exact amount —SWEEPER_ABIandSWEEPER_FACTORY_ABIchange shape: the tuple gains a leadingamount, 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 newStaleQuote.amount: 0nbinds nothing and is accepted only on the direct CCTP route, whose burn is exact-amount — every other route rejects it with the newQuoteNotBound. Calldata encoded against the previous tuple is rejected
Changed
- One order listing for every kind of order —
GET /api/v1/orders(gRPCListOrders;sdk.trading.getPaginatedOrders/iterateOrders) returns book orders, TWAP parents and conditional orders in one timestamp-sorted stream, sostatus=SUBMITTED,PARTIALLY_FILLEDis the working set across all three kinds in one call.orderTypegains two response-only values,TWAPandCONDITIONAL, and the matching row carries its full detail under the newtwap/conditionalfields with the shared fields populated from it. In@0xmonaco/types,Order.orderTypewidens fromOrderTypetoListedOrderType— an exhaustive switch must handle the two new values. Creation still takesLIMITorMARKET; the WebSocketorderschannel 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;FORBIDDENfor a delegated item without a persisted original) while the valid items proceed and the failed item’s original is untouched, matching batch create. A400still means the batch did not execute, but a malformed item no longer produces one — so readresults[]per item, because a2xxcan carry per-item failures. Empty and oversize batches are the only input validation that still rejects the whole request.totalRequested === totalSucceeded + totalFailednow holds on both batch endpoints - Authenticated reads draw a per-account budget — every signed
GET(plus thePOSTorder-risk previews) and everyGet*/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 return429RATE_LIMIT_EXCEEDEDwithRetry-After(gRPCRESOURCE_EXHAUSTEDwithRetryInfo) 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.
CancelOrderand the batch cancel forms now refuse it and name the parent to cancel instead;CancelTwapOrderremains 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-riskreflects the same release. Request fields are unchanged and the rollout defaults to off
Monaco Protocol SDK v1.0.56
Added
GET /api/v1/sweeper/chains— a public, unauthenticated listing of the chains and assets the sweeper watches: per chain its EVMchainId, shortname, ahubflag for the settlement chain, and the swept assets withsymbol,tokenAddress,decimals, andminSweepRaw(raw base units, as a decimal string).sdk.sweeper.getChains()in TypeScript,SweeperService.GetChainsover gRPC. Registration is unchanged — the deposit address is identical on, and watched on, every listed chain- Durable version on spot balance rows —
versionappears on the spot balance REST reads, the gRPCAccountBalance, the subscribe-timebalancessnapshot, and livebalance_updateevents, and carries the same value on each for identical state. Apply a higher version, treat equal as an idempotent duplicate, ignore lower;0and an absent field both mean unranked. Margin collateral is deliberately unversioned and must be handled fail-open.useUserBalancesin@0xmonaco/reactdoes the ranking for you MESSAGE_RATE_LIMITWebSocket error code —WebSocketErrorCodegains it; existing codes are unchanged
Changed
- Order rate limits tightened —
reduceOnlyorders 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-alland its by-pair form; gRPCBatchCancelAll/BatchCancelAllByPair) and TWAP creation (POST /api/v1/orders/twap; gRPCCreateTwapOrder) are metered at one item per call and declare429/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/-minutereport 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
Errorframe withMESSAGE_RATE_LIMIT, then the connection closes with1008. ProtocolPingcounts;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
0instead 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_ARGUMENTeven though a plain margin order provisions the bucket at admission.CROSSnow provisions the cross bucket allocation-neutral;ISOLATEDseeds 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.riskBucketIdnames the bucket.reduceOnlyparents still require an existing bucket
Monaco Protocol SDK v1.0.55
Added
- Opt-in RFC 9421 request signing — REST session authentication accepts RFC 9421 HTTP message signatures (
Signature-Input,Signature,Content-Digest) over the same Ed25519 session keys, alongside the legacyX-Monaco-*headers, which stay supported with no retirement date. Monaco verifies a bounded profile: labelmonaco, the four covered components@method,@path,@query,content-digestin that order,createdin Unix seconds within 30 seconds of server time,keyidas the 64-hex session public key, and RFC 9530 SHA-256Content-Digestover the exact body. Presence ofSignatureorSignature-Inputselects RFC verification with no fallback to legacy, and pairing either with anX-Monaco-*header is rejected401.Content-Digestalone 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.RequestSigningis exported from@0xmonaco/typesClient::new_authenticatedin 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 onwasm32. Existing unsigned constructors are unchanged,RequestSigning::Legacyselects the legacy headers, andSessionSigner::sign_atsigns a reusablereqwestrequest
Fixed
- Authentication declared on 24 previously undeclared endpoints — the specification gains a
monacoHttpSignaturescheme next tomonacoSignature, and 24 operations that declared no security at all now declare both: seven under/api/v1/positions, all fourteen under/api/v1/margin, and the three conditional order endpoints. The gateway always required a session signature on them, so this corrects the published contract rather than the enforced one — but regenerate any client built from the specification, which was not signing them. A test now asserts the specification’s declared authentication against the gateway’s route table - Request-body buffering scoped to signed routes — the gateway no longer buffers and hashes every request body globally. Signed routes still read and hash theirs under the same 2 MiB bound; public and backend-key routes use their normal extractor limits. No request or response shape changes
Monaco Protocol SDK v1.0.54
Breaking
totalTradeCountLtdretyped from string to integer — corrected tointeger/int64for 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/typesnow declaresnumber | nullinstead ofstring | nulland the generated Rust SDK an integer instead of aString, so update string-specific uses (parseInt,Stringmethods,string-typed assignment); generic formatting such asString(...)or interpolation still compiles
Changed
- Keyed submission capacity and availability codes —
ORDER_SUBMISSION_CAPACITY_EXCEEDEDrefuses a newidempotencyKeywhile retained receipts are at capacity;ORDER_SUBMISSION_UNAVAILABLEreports 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 HTTP503/ gRPCUNAVAILABLE, 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 andIDEMPOTENCY_KEY_CONFLICTare unchanged - Per-account order rate limit declared — order endpoints document
429/RATE_LIMIT_EXCEEDEDwithRetry-Afteranddetails.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 carryX-RateLimit-Limit-Second/-MinuteandX-RateLimit-Remaining-Second/-Minute. Enforcement rolls out per environment; until then over-budget requests are admitted and only measured tokenSymbolon margin-collateral balance frames —balance_updatereportstokenSymbolfor reasonstrade,fee,rebate,funding,liquidation, andadl, which could previously arrive null after a matching-engine restart. Still typedstring | null; null now means only an unbound collateral token or a token that is not a configured assetpostOnlyon maker fills —OrderFilledandOrderPartiallyFilledwithrole: "maker"carrypostOnly: truefor a post-only resting order, the last order-event variant to gain the flag. Same absent-rather-than-false contract, sodata.postOnly ?? falsereads 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_ALLOWEDbefore 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
1013on every subscription mix, includingpositions,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_updatewhen 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.profilestats and chart methods may reflect replica lag, and return503/Unavailableon a reader outage rather than falling back to the writer. Portfolio margin withdrawal bounds and trade history keep their freshness requirements
Monaco Protocol SDK v1.0.53
Breaking
- Sweeper derivation uses the factory forwarder —
predictSweeperAddressandsweeperCloneInitCodetakeforwarder, notimplementation;SweeperAddressInput.implementationis 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
selectedTradingPairIdsfromtransferCollateralToRiskBucket({ 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 estimatedFeeestimates 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.clientOrderIdremains a separate correlation label - Typed WebSocket baselines — stateful
sdk.wssubscriptions take an optional trailingonSnapshotcallback and deliver an available subscribe-time baseline as one typed array, separate from live events.useInstruments,useUserBalances, anduseUserOrdersuse it for reconnect repair;@0xmonaco/typesaddsSnapshotHandler<T>andOrderSnapshotItem - Portfolio risk-bucket scope —
PortfolioRiskBucketadds requiredmarginModeand isolated-onlytradingPairId, 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/ gRPCUnavailable; a mutation can still commit after timeout, so reconcile before retrying and reuse the same order-createidempotencyKey. KeyedbatchCreateinstead returns overall REST200/ gRPCOKwithSERVICE_UNAVAILABLEon the expired item’sresults[].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 —
isolatedMarginon open isolated REST/gRPC reads, position snapshots, and live updates reflects allocated bucket collateral after settled PnL, funding, and open-order reserves rather than stale posted margin
Monaco Protocol SDK v1.0.51
Breaking
- Order fill fees use decimal quote units —
feeonOrderFilled,OrderPartiallyFilled, andOrderMatchedis now the normalized decimal quote-asset amount; stop dividing it by10^quoteDecimals. NewfeeRawcarries 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/makerOrderIdand fill-eventtotalFilled; useorderId/tradeId, cumulativefilledQuantity, and incrementallastFillQuantity.OrderMatcheddistinguishes required execution fields, present-null trade/price/slippage fields, and optional last-fill fields;OrderExpiredfill, 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 closedeventTypediscriminants
Added
- WebSocket
onErrorcallback — connection-scopedErrorframes now reachonError({ code, message }), with exported error types and unknown-code passthrough.AUTH_REQUIREDneeds authentication, release of the last local channel handler, and resubscription;SESSION_INVALIDneeds re-authentication;SNAPSHOT_UNAVAILABLEneeds 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 whensnapshot.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 throughsdk.profile.getUserTradesand missing locally resting orders through targetedsdk.trading.getOrderreads - 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, anduseUserOrderspreserves 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 missingOrderMatchedexecution fields
Changed
- Fixed UTC microseconds on selected order timestamps — ordinary-order
createdAt/updatedAt/expirationDate, every conditional-order timestamp, persisted TWAP timestamps, and application order-viewcreatedAt/updatedAtuse six fractional digits andZ. Ordinary-order lifecycle stamps retain variable fractional width, and the formatting change does not make order detail/listupdatedAtclocks comparable batchCancelAllcaps requests at 20,000 active orders — over the cap, cancels nothing and returns REST400/ gRPCInvalidArgument
Fixed
- WebSocket parser preservation — movements now deliver
symboland 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 —
useUserOrdersnarrows 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 usesupdatedAt, notversion - Partial portfolio withdrawal valuation — unpriceable external withdrawals are excluded from withdrawal and dependent deposit totals with
valuationStatus: "partial"on a successful response. Recognized quote assets remain unit-priced; reconciliation errors can still fail the request - Stable subscription paging — keyset paging prevents duplicate or skipped order, conditional-order, and position snapshot rows when concurrent updates move page boundaries
- Liquidation maker-close cleanup — closing a position via a liquidation maker fill cancels remaining reduce-only orders as
POSITION_CLOSED, releases reserves, and preserves history/PnL enrichment - Resilient fee refresh — bounded timeouts, a repeatable-read snapshot, last-known-good retention, and retry prevent transient refresh failures from freezing updates or mixing fee tiers
Monaco Protocol SDK v1.0.50
Breaking
- Sweeper registration is authenticated and carries no identity —
sdk.sweeper.register(POST /api/v1/sweeper/register; gRPCSweeperService.Register; Rust RESTregister_deposit_address) now requires a session, andclientIdanduserAddressare 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 answer401, an inactive application403. 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 theretryAftervalue rather than assuming a window. The request gains an optionaldepositTarget("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; gRPCTransferCollateralToRiskBucket; thetransfer_collateral_to_risk_bucketMCP tool) takes a new optionalapplyToPositionMargin, and the default is a behavior change. Omitted ortrueis 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.falseis 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 explicittrue, while the TypeScript request type and the MCP tool schema reject the field however it is set — omit it entirely there rather than passingfalse. Either behavior improves the liquidation cushion identically. The response gains an optionalpositionMarginCredited—truethe credit landed,falseit 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; gRPCTransferCollateralToMarginAccount) is deprecated in TypeScript (@deprecated) and in the OpenAPI operation (deprecated: true). It overloads two unrelated operations behind one route: withtradingPairIdit allocates into that pair’s isolated risk bucket, without it it deposits from the wallet into the parent margin account. UsetransferCollateralToRiskBucketfor bucket funding — which now applies the same position-margin credit — andtransferCollateralToParentMarginAccountfor wallet-to-margin deposits. It keeps working; no removal date scheduled
Added
- The deposit-address encoder is exported from
@0xmonaco/contracts—predictSweeperAddressaccepts an optionaldepositTarget("spot"default, or"margin"), andencodeDepositApplicationData(clientId, target)plus theDepositTargettype 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.encodeDepositApplicationDatais the canonical encoder for a deposit’s on-chainapplicationData— the bytes a sweeper address is a hash of — so@0xmonaco/coreconsumes it internally and@0xmonaco/typesre-exportsDepositTargetfrom 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
marginRatioon theaccountWebSocket channel saturates on dust equity too — the documented contract ofAccountEvent.marginRatiowidens: 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 truemaintenance / equityexceeds the decimal range, not only an account still owing one at zero or negative equity. An account with no maintenance requirement still reports0, unchanged. The producer previously crashed on the dust-equity state instead of emitting a frame. No type change — clamp the sentinel for display and treat any value at or above the liquidation threshold as maximal distress
Monaco Protocol SDK v1.0.48
Breaking
ConditionalOrderEventDatadeclares the values the server can really send —timeInForcewidens from"GTC" | "IOC"to the fullTimeInForceunion (the field mirrors an unconstrained server column mapped exhaustively over every variant), andreasonwidens from theConditionalOrderEventReasonunion tostringbecause the wire field is a free-form server string.ConditionalOrderEventReasonis 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 adefaultbranch to keep compiling. See Conditional Orders Channel
Added
availableOrderCollateralon risk-bucket rows — an optional field on the rows returned bygetMarginAccountSummary,getParentMarginAccountSummary, andlistMarginAccounts(GET /api/v1/margin/accounts/{margin_account_id},GET /api/v1/margin/parent-margin-account,GET /api/v1/margin/accounts; gRPCMarginAccountsService), and on theriskBucketsrows fromsdk.portfolio.getMargin()(GET /api/v1/accounts/me/portfolio/margin; gRPCAccountsService.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. UnlikewithdrawableCollateralit credits an unrealized gain in full, and it already contains the bucket’s live free collateral — do not addfreeCollateralorunrealizedPnlon top. Not additive across rows (every bucket’s figure includes the same parent headroom), and a requirement budget rather than an order size: a reducing, reduce-only, or sheltered order needs less of it or none, while an order crossing a losing position can need more than its own margin plus fees. Present only on rows served from a live risk engine snapshot; absent (key omitted, never null) on parent rows and persisted-fallback reads, and truncated toward zero at quote decimals on the portfolio surface. See Margin Accounts and Perps CollateraluseInstrumentsReact hook —useInstruments(tradingPairId?)is a typed hook over the publicinstrumentWebSocket channel. It subscribes throughsdk.ws.instruments(), applies the subscribe-time snapshot and every subsequent change frame as a terminal-state entry per market, and unsubscribes on unmount.listing,delistingandconfig_changeare the kinds emitted today;haltandunhaltare reserved for a future market-regime producer. Returnsinstruments(anInstrumentEventData[]ordered by symbol, then trading pair id),instrumentsByPairkeyed by that id, plussubscribed,errorandclearError. Omit the argument to follow every market; delisted markets stay in the list withisActive: falserather than disappearing. SeeuseInstruments
Fixed
- The
conditionalOrdersparser no longer drops whole frames on an unexpected value — an unexpected enum value in aconditional_order_updateframe used to throw inside the WebSocket message handler and lose the entire update silently; atimeInForceofFOKorGTD, or anyreasonthe server added, was enough.orderType,timeInForce,state,side,positionSide,conditionType,triggerSource,associationTypeandreasonare now read leniently and passed through, matching the position, liquidation and market-stats parsers.event_typestays strict, so a mis-routed frame is still rejected. Match these fields with adefaultbranch
Monaco Protocol SDK v1.0.47
Added
PASSIVETWAP execution style —sdk.trading.createTwapOrder()takes an optionalexecutionStyle("TAKER" | "PASSIVE", default"TAKER"— the existing behaviour, unchanged).PASSIVErests each slice as a post-onlyLIMITGTCchild at the same-side touch, chases the touch with reprices throttled to one attempt per second, and sweeps only the remainder as a bandedMARKETchild at the slot deadline; a target price that would cross executes immediately as a banded taker instead. The parent’slimitPriceis never breached, and neither is itsslippageToleranceBpsband 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’slimitPricebecomes the only bound on the resting quote. SetlimitPriceon 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.executionStyleis on the read model, and thetwap_ordersWebSocket channel addspassive_child_placed,child_repriced, andpassive_fill(sweeps still arrive asslice_placed). See Passive Execution- Conditional (price-triggered) TWAP parents —
createTwapOrder()acceptstriggerPrice+triggerDirection(ABOVE/BELOW, both-or-neither) to hold a parentPENDINGuntil the reference price reaches the level, plus an optionalconditionalExpiry(RFC 3339 UTCZonly, future, defaulted and capped at create + 30 days, valid only with a trigger) that terminalizes an unfired parent asCANCELLEDwith terminal reasontrigger expired. A conditional create takesdurationSecondsonly —startTime/endTimeare rejected — and runs a floating window anchored at activation. A trigger already satisfied at create is a400, 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, andtriggeredAtare on the read model and the WS payload, which adds thetriggeredandtrigger_expiredreasons. See Conditional TWAP sequenceon theaccountWebSocket channel — each frame carries a monotonic per-margin-account publish counter within one producer run. Dedupe byupdatedAt(newest wins) and usesequenceonly to break ties between frames sharing anupdatedAt. 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
postOnlybatch-replace item no longer costs the original order —batchReplacenow 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 reportserror.code === "POST_ONLY_WOULD_CROSS"; only the fate of the original changed, bringingbatchReplaceinto line with singlereplaceOrder. Four narrow cases remain, none visible before the cancel pass: crossing a replacement an earlier item just rested, crossing an original that failed to cancel, the same original listed twice, and an earlier non-post-only item that would have taken the touch. Every other per-item failure — insufficient balance, self-trade outside the batch, risk rejection — still leaves the original cancelled. See Batch Replace - Funding payments appear in the movements feed —
GET /api/v1/accounts/movements(sdk.profile.getPaginatedUserMovements()) unions funding payments in at read time asfundingtransactions. Filter them withtransactionType=FUNDINGthrough 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-casetransactionType=fundingbecause the server matches case-insensitively. A funding movement is a collateral USD delta rather than a token transfer:amountis a magnitude with the direction inentryType(DEBITpaid,CREDITreceived — REST movement rows keep the upper-case enum values),decimalsis0, andamountRawequalsamount. It shares itsidwith the livemovementsWebSocket frame so a client consuming both dedupes byid, but the payloads differ: REST is upper-case and resolvesassetId/symbol/decimals, the WebSocket frame is lower-case with no resolved asset id. Zero-amount funding windows are excluded. See Account Balances MonacoWebSocketandMessageHandlermoved to@0xmonaco/types— both are now re-exported from@0xmonaco/corerather than defined there, making@0xmonaco/typesthe single source of truth. The public surface is unchanged and existing@0xmonaco/coreimports keep working
Fixed
MonacoSDK.wstypes the full channel surface —createMonacoSDK()instances carried a stale inline copy of the WebSocket type, hidingpositions,liquidations,account,instruments,marketStats, andmarketStatsAllfromsdk.wsand rejectingws.ohlcv’s optionalpriceTypeargument even though the runtime supported them.sdk.wsis now typed as the fullMonacoWebSocketinterface; runtime behaviour is unchanged- Corrected
clientOrderIdguidance — 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 aLIMITthat 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 MCPplace_limit_order/place_market_order/replace_orderdescriptions carry the same correction, and MCP exposes no order-stream tool, so an agent must report an ambiguous outcome rather than retry. No wire, schema, or behaviour change
Monaco Protocol SDK v1.0.46
Added
- TWAP transaction cost analysis —
sdk.trading.getTwapOrderTca(twapOrderId)(GET /api/v1/orders/twap/{twapOrderId}/tca; gRPCOrdersService.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) andbenchmarkSourcenaming the series (risk_mark_1m, orohlcv_1m_closefallback). The six analytics fields are omitted from the body — key absent, notnull— when they cannot be computed honestly; a non-terminal parent returns400, and another user’s parent is a404identical to an unknown ID.@0xmonaco/typesaddsGetTwapOrderTcaResponse - Typed
sdk.ws.instruments()WebSocket subscription — a typed helper for the publicinstrumentchannel 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). OmittradingPairIdfor all markets or pass one to filter; handlers receive the typedInstrumentEvent, every frame is a full-config terminal-state snapshot withchangedFields, and the subscribe-time active-pair baseline is fanned out as one event per market before the live diffs (late subscribers on an already-held channel are replayed a locally cached baseline). Previously the channel was reachable only over a raw WebSocket connection
Monaco Protocol SDK v1.0.45
Added
- Public
instrumentWebSocket 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 toinstrument:<tradingPairId>for one market or the bareinstrument(instrument:*) for every market; each liveEventframe carries one market’s full current config — consume it as a per-market snapshot, not a delta — withchangedFieldsnaming the touched fields, and the subscribe-time baseline arrives as a singleSnapshotframe whosedatais an array of those config objects.@0xmonaco/typesshipsInstrumentEvent/InstrumentEventData/InstrumentChangeKind; there is no typedsdk.wshelper, so consume the raw channel. See WebSockets cumFeeson positions —sdk.positions.listPositions()/getPosition()(GET /api/v1/positions,GET /api/v1/positions/{positionId}; gRPCPositionsService.ListPositions/GetPosition) addcumFees(signed decimal string) toPosition: 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. UnlikefeesPaid(closing-side only), it is the whole-life figure;nullwhen the opening sample or a reducing row’s fee is missing — but, unlikefeesPaid, 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 —
netRealizedPnlandrealizedRoeon 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 (withcumFeesnull) - Faucet history returns one entry per on-chain deposit —
recentRequests(faucet info) now returns a separatemintedentry for the spot and margin legs of a mint, each with its ownamount(and atxHashonce that leg confirms) and the margin leg labelledUSDC (margin), instead of squashing a session into a single 2000 entry that carried only the spot transaction hash
Monaco Protocol SDK v1.0.44
Added
asErc20deposit option for the wrapped-native asset —sdk.vault.deposit()acceptsoptions.asErc20(@0xmonaco/core;DepositOptions/DepositSchemain@0xmonaco/types) to deposit the chain’s wrapped-native asset (e.g. WSEI) as its ERC20 through approve +depositERC20, instead of the native-coindepositNativedefault from 1.0.43;sdk.vault.needsApproval()takes the same option and reads the real WSEI allowance.optionsis the fifth argument todeposit()and the third toneedsApproval(); the ERC20 path is not auto-approved —deposit()throws aContractError(code === "CONTRACT_ERROR",revertReason === "APPROVAL_REQUIRED") if the allowance is shortwithdrawableCollateralon portfolio margin —sdk.portfolio.getMargin()(GET /api/v1/accounts/me/portfolio/margin; gRPCAccountsService.GetPortfolioMargin) addswithdrawableCollateral(decimal string) toPortfolioMarginTotaland eachPortfolioRiskBucket(@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. UnlikeparentAvailableCollateral/freeCollateralit 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}; gRPCOrdersService.ReplaceOrder) now treatsnewOrder.quantityas the order’s new total: a partially filled order reststotal - filledand locks for that remainder only (FIXLeavesQty = 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 withquantity: "200"rested 200 — it now rests 104 and reads 96/200 filled. Omittingquantitykeeps the total; unfilled orders and margin reduce-only replacements are unaffected;batchReplaceitems 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
repositoryfield and setshomepageto the public docs (the old private-repo links returned 404 outside the org): the five npm packages tohttps://docs.0xmonaco.com/sdk/typescript, and the Rust cratesmonaco-sdk/monaco-grpc-sdktohttps://docs.0xmonaco.com/sdk/rust-rest/https://docs.0xmonaco.com/sdk/rust-grpc. No code or type changes — this is why@0xmonaco/react,@0xmonaco/contracts,@0xmonaco/mcp-server, and both Rust crates bump to 1.0.44 with no behavior change
Monaco Protocol SDK v1.0.43
Added
- TWAP parent orders — submit one server-side TWAP parent and the matching engine slices it into market child orders across a time window.
sdk.trading.createTwapOrder/getTwapOrder/listTwapOrders/cancelTwapOrder(POST /api/v1/orders/twap,GET /api/v1/orders/twap/{twapOrderId},GET /api/v1/orders/twap,POST /api/v1/orders/twap/{twapOrderId}/cancel; gRPCOrdersService.CreateTwapOrder/GetTwapOrder/ListTwapOrders/CancelTwapOrder). Window is exactly one ofdurationSecondsorendTime, pluslimitPrice/slippageToleranceBps/randomize/ margin context / aclientTwapIdidempotency key (duplicate →409). Live progress streams on the authenticatedtwap_orderschannel viasdk.ws.twapOrders(handler, tradingPairId?);@0xmonaco/typesadds the full type set (TwapOrder,TwapOrderState,CreateTwapOrderOptions,TwapOrderEvent, …). Delegated-agent sessions are rejected. See TWAP Orders - In-place partial cancel —
sdk.trading.cancelOrder(orderId, { remainingQuantityTarget })(CancelOrderRequest.remainingQuantityTarget; gRPCremaining_quantity_target) reduces a resting order in place to the target remaining quantity, keeping its queue position — unlikereplaceOrder, 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-terminalOrderPartiallyCancelledevent announces the reduction on theorderschannel - Mark-price candles on the OHLCV WebSocket channel —
sdk.ws.ohlcv(tradingPairId, tradingMode, interval, handler, priceType?)accepts an optionalpriceType("trade"/"mark"/"*")."mark"is the close-only mark-price series for margin pairs (open=high=low=close=mark, volume0), published on the1minterval only; the token is a fifth channel segment, so existing four-token subscriptions are unchanged.@0xmonaco/typesaddsOHLCVPriceTypeand an optionalOHLCVEvent.priceType wrappedNativebalance flag —GET /api/v1/accounts/balances+/balances/{asset_id}(gRPCGetBalances/GetBalanceByAsset) return an optionalwrappedNativeboolean per balance (AccountBalance.wrappedNative),truefor 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; gRPCGetPortfolioAssets) surfaces four optional spot cost-basis fields per asset row —avgCost,unrealizedPnl,unrealizedRoe,cumRealized. Populated when a cost-basis checkpoint exists;unrealizedPnl/unrealizedRoealso require a mark price, andunrealizedRoeis 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’swrappedNativeflag instead of a zero token address.sdk.vault.deposit()routes awrappedNativeasset (e.g. WSEI) to the payabledepositNativewithamountasmsg.value— no ERC20 approval required or checked — while every other asset keeps approve +depositERC20;sdk.vault.needsApproval()returnsfalsefor 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 viasdk.positions.attachPositionTpSl()(POST /api/v1/positions/{positionId}/tp-sl; gRPCAttachPositionTpSl) now cancels the position’s previously active full-close legs of the same condition type atomically in the same engine step (each old leg emitsconditional_order_updatereason: "cancelled"before the replacement’s"created"), so moving a level is one call with no both-or-neither window. OCO siblings, fixed-quantitylegs, and entry-attached legs survive
Fixed
marginRatiofor accounts at zero or negative equity —GetPositionRisk(GET /api/v1/positions/{positionId}/risk) and theaccountWebSocket channel (AccountEventData.marginRatio) now report the maximum-distress ratio for accounts at non-positive equity instead of0(equity exactly zero) or a negative ratio that sorted as safer than a healthy account. Accounts with no open positions still report0. The sentinel is a very large decimal string (Decimal::MAX, ~7.9e28) — clamp it for display
Monaco Protocol SDK v1.0.42
Added
- Typed
market_statsandaccountWebSocket helpers —@0xmonaco/coregainssdk.ws.marketStats(tradingPairId, handler)andsdk.ws.marketStatsAll(handler)(public) andsdk.ws.account(handler)(authenticated), so themarket_statsandaccountchannels no longer need the raw WebSocket protocol.@0xmonaco/typesaddsMarketStatsEvent/MarketStatsData/MarketStatsAllEvent/MarketStatsSummaryand theMarketStatusunion; perp-only fields are optional so spot flows through the same helpers. Theaccountproducer stays gated off by default, sosdk.ws.accountsubscribes but frames arrive only once it is enabled - Mark-price candlestick series — the candlestick endpoint accepts an optional
priceType(GetCandlesticksParams.priceTypein@0xmonaco/core/@0xmonaco/types; thepriceTypequery onGET /api/v1/market/pairs/charts/candlestick/{tradingPairId}/{interval};GetCandlesRequest.price_typeon gRPCMarketService.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 is0."mark"on a spot pair returns a400
Changed
clientOrderIdis unique among your resting orders — a single create or replace whoseclientOrderIdis already held by another of your own resting orders is rejected with409/ gRPCALREADY_EXISTS, codeCLIENT_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 returns200/OKwithCLIENT_ORDER_ID_CONFLICTon the offending item’sresults[].error.code, so inspectresults[]. 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 wasMARKET/IOC/FOK, holds no handle and the resubmission can execute again. Scope is per user among resting orders only; the value frees on terminal state and a replacement may restate it
Monaco Protocol SDK v1.0.41
Added
clientOrderIdcorrelation handle on order placement —placeLimitOrder/placeMarketOrder/replaceOrder/batchCreate/batchReplace(@0xmonaco/core;POST /api/v1/orders,PUT /api/v1/orders/{orderId},.../batch-create,.../batch-replace; gRPCCreateOrderRequest/ReplaceOrderRequest/BatchCreateOrderItem/BatchReplaceOrderItem; theplace_limit_order/place_market_order/replace_orderMCP tools) accept an optionalclientOrderId(≤64 chars fromA-Za-z0-9._:-), echoed on order detail/list reads and everyordersWebSocket 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 with400/INVALID_ARGUMENT(batch-create per item withINVALID_CLIENT_ORDER_ID). Uniqueness was not enforced in this release; v1.0.42 added a guard, scoped to resting orders only- Order-attached TP/SL legs size independently — the
takeProfit/stopLosslegs attached to an order at placement (ParentTpSlLeg) gainquantityandclosePosition, the same sizing contract as position-attachedTpSlLeg; previously they were frozen at the parent order’s quantity. Omit both (orclosePosition: true) to track the live position, or name aquantityfor a partial that never resizes accountWebSocket channel — a new authenticated channel (subscribe withaccount) for a margin account’s health — equity, collateral, free collateral, margin ratio, maintenance requirement (decimal strings) plus the margin account id and timestamp — via anaccount_updateevent. The channel routing and theAccountEventtype 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 (nosdk.wshelper): the wire is snake_case (event_type,margin_account_id,free_collateral, …) andAccountEventin@0xmonaco/typesis 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 snapshotmovementsframes for on-chain deposits & withdrawals — deposits and withdrawals now emit auser_movementframe (transaction typedeposit/withdrawal) co-ordered with theirbalance_update, so themovementsandbalancesstreams line up. TheUserMovementwire shape is unchanged
Fixed
- Funding an isolated risk bucket no longer ratchets a sole position’s margin —
transferCollateralToRiskBucket()(@0xmonaco/core;transfer_collateral_to_risk_bucketMCP tool; RESTPOST /api/v1/margin/risk-buckets/collateral/transfer-in; gRPCTransferCollateralToRiskBucket) 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 theaddPositionMarginreplacement; to increase a sole position’s stored margin usetransferCollateralToMarginAccount(parentMarginAccountId, { asset, amount, tradingPairId })instead
Monaco Protocol SDK v1.0.40
Breaking
addPositionMargin/reducePositionMarginremoved —positions.addPositionMargin()/reducePositionMargin()(@0xmonaco/core), theperpRoutes.positions.addMargin/reduceMarginhelpers,usePositions()’s two methods (@0xmonaco/react), theadd_position_margin/reduce_position_marginMCP tools, RESTPOST /api/v1/positions/{position_id}/margin/addand.../margin/reduce, and the gRPCPositionsService.AddPositionMargin/ReducePositionMarginRPCs are all removed — they sourced collateral from the spot wallet, bypassing the parent margin account. Migration: allocate withtransferCollateralToRiskBucket({ ..., marginMode: "ISOLATED" }), release withtransferCollateralFromMarginAccount(parentMarginAccountId, { ..., tradingPairId })- WebSocket position status is SCREAMING_SNAKE_CASE — position events on the
positionschannel carrystatusasOPEN/LIQUIDATING/CLOSED/LIQUIDATED/EXPIRED, matching REST and@0xmonaco/types, instead of the previous PascalCase (Open).PositionEventStatusis narrowed; the event data also gains optionaltradeId/orderId
Added
marginModeon collateral transfer-out —marginAccounts.transferCollateralFromMarginAccount()(TransferCollateralRequest; RESTPOST /api/v1/margin/accounts/{margin_account_id}/collateral/transfer-out; gRPCTransferCollateralFromMarginAccountRequest.margin_mode) accepts an optionalmarginMode.marginMode: "CROSS"addresses a cross risk bucket thattradingPairIdcannot name; the two selectors are mutually exclusive- Dollarized open interest & 24h volume —
getPerpMarketSummarygainsopenInterestBase,openInterestNotional,quoteVolume24handgetMarketMetadatagainsquoteVolume24h;getScreenergainsopenInterestBase/openInterestNotionalperScreenerItem(bothnullfor spot,"0"for a margin pair with no open positions).openIntereststays a base-asset alias market_statsWebSocket 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 amarket_statuslabel). 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 counterpartyOPENrefreshes, and areason: "funding"position_updateon funding settlement; thebalance_updatereasonunion gainsfee/rebate/funding/margin_deposit/liquidation/adland now fires for resting makers (with a separaterebateframe); funding surfaces onmovementswithtransactionType: "funding"
Changed
- Margin shortfalls no longer read “Insufficient balance” —
InsufficientInitialMargin/InsufficientFreeCollateralnow surface asinsufficient initial margin: .../insufficient free collateral: ...(400, type unchanged); genuine wallet shortfalls still readInsufficient 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 treatslimitas a hard cap that counts gap-filled bars. Defaults (350), clamp (500), and pagination direction are unchanged - Consistent order-event fill quantities —
filledQuantityis always the cumulative order fill; a new optionallastFillQuantity/lastFillQuantityRawcarries the per-event increment; the IOC/market partial-cancel event now carriesfilledQuantity(legacytotalFilledretained as alias);cancelledAtis RFC3339 withZandaverageFillPriceis now typed - Batch requests capped at 100 items —
BatchCreateOrdersSchema/BatchReplaceOrdersSchemareject arrays over 100 client-side (MAX_BATCH_ORDER_ITEMSexported fromvalidation/trading); REST/gRPC reject an oversized batch with400 At most 100 orders per batch request, and sequencer overload may return a retryable503OVERLOADED tokenSymbolon deposit/withdrawal balance events — thebalance_updateevent now carries the registrytokenSymbolondeposit/withdrawal(wasnull); type unchanged (string | null)
Fixed
- WebSocket movement event parser — reads the serialized
tokenkey (with atoken_addressfallback) and surfacesstatus/assetId/blockNumber;UserMovementEventDatanow declares them (tokenAddresskept as a populated back-compat alias) - IOC position closes accept & require
limitPrice—ClosePositionSchemaand theclose-positionMCP tool now requirelimitPriceforLIMITandIOCcloses (forbidden only forMARKET), 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 advertisedGTDare corrected toGTC. For a timed order sendtimeInForce: "GTC"withexpirationDate
Monaco Protocol SDK v1.0.39
Breaking
latest_orders/latest_movementsremoved from paginated responses —GetPaginatedOrdersResponseandGetPaginatedUserMovementsResponse(@0xmonaco/types, surfaced throughsdk.trading.getPaginatedOrders/sdk.profile.getPaginatedUserMovements) drop the deadlatest_orders/latest_movementsfields — a live-engine hot-cache shadow the backend stopped populating in v0.8.7, so callers have receivedundefinedfor them since.@0xmonaco/react’suseUserOrders/useUserMovementsnow seed initial state from the paginatedorders/movementsarrays; hook return shapes are unchanged. Migration: read theorders/movementsarrays directly, and refetch a fresh snapshot on reconnect/resync
Added
- Delegated-owner mode for the MCP server —
@0xmonaco/mcp-servergainsMONACO_DELEGATED_OWNER_USER_ID, a fail-closed startup option that runs the server on behalf of a delegated owner account. TheMONACO_PRIVATE_KEYwallet 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. RequiresMONACO_PRIVATE_KEY+MONACO_CLIENT_ID, rejects a blank value, and never falls back to trading as the agent’s own account. See Delegated owner mode
Fixed
useTokenLifecycledocs clarified to session-key vocabulary — the@0xmonaco/reacthook’s JSDoc, inline comments, and log strings now describe the authenticated ed25519 session (persisted and refreshed viasdk.refreshAuth()) instead of implying JWT/bearer tokens; Monaco is a tokenless session-key exchange. Exported members and config fields keep their legacy “token” spelling, so the public API is unchanged, and the hook’s reference page is now linked from the docs navigation
Monaco Protocol SDK v1.0.38
Added
- Typed positions & liquidations WebSocket streams —
sdk.ws.positions(handler, tradingPairId?)andsdk.ws.liquidations(handler, tradingPairId?)(@0xmonaco/core) parse each frame into a typedPositionEvent/LiquidationEvent(@0xmonaco/types). Positions conflate (no gap signal); liquidations shed a slow subscriber with close code1013— treat it as the gap signal and resync from a snapshot. See WebSockets - WebSocket reconnect/resync on the SDK config —
SDKConfiggainsws?: MonacoWebSocketConfig, soonStatusChange/onResyncand the reconnect knobs (autoReconnect,maxReconnectAttempts,reconnectBaseDelayMs,connectionTimeoutMs) can be passed tonew MonacoSDK({ ..., ws })and are forwarded to the internal client — no longer only reachable through the low-levelcreateMonacoWebSocketfactory postOnlyon order reads —GetOrderResponse(order detail and each list item) echoes an optionalpostOnlyboolean, sosdk.trading.getOrder/getOrders, RESTGET /api/v1/orders/GET /api/v1/orders/{order_id}, and gRPCOrdersService.GetOrder/ListOrdersnow report whether an order was placed post-only. Absent for orders placed before the flag was persisted- Structured
POST_ONLY_WOULD_CROSScode 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’scodefield and as a gRPCgoogle.rpc.ErrorInforeason, matching what the batch endpoints already returned. Match on the code; the message substring is now a legacy fallback order_idfilter on gRPC user trades —TradesService.ListUserTrades(authenticated, gRPC-only) adds an optionalorder_idUUID 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/reactaddsuseRewardsBalance,useTraderCodeInfo, anduseTransferRewards, thin wrappers over the matchingsdk.pitpass.*methods. See PitPass hooks
Fixed
withdrawableCollateralreports a withdrawable number —withdrawableCollateral,newWithdrawableCollateral, andmarginAvailableCollateral(across the margin account / risk bucket summaries, transfer responses, andGET /api/v1/margin/available-collateral) now report the largest amount the withdrawal gate will accept instead offreeCollateral, 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.orderssendtradingPair,filledQuantity, andfee, not the never-populatedsymbol/executionQuantity/makerFee; the RAW precision fields (quantityRaw,filledQuantityRaw,remainingQuantityRaw,totalFilledRaw) are now typed, andexecutedAtis RFC3339 with aZsuffix.useUserOrdersreads its pair fallback fromdata.tradingPair applications/config401 description — corrected from “Unauthorized - Invalid or missing JWT” to “Authentication required”; the endpoint is session-authenticated and never used JWT. Description-only
Monaco Protocol SDK v1.0.37
Added
- Post-only orders —
postOnly?: booleanonsdk.trading.placeLimitOrder/replaceOrder/batchCreate/batchReplace(and theuseTrade()equivalents), the RESTcreate_order/batch_create_orders/replace_order/batch_replace_ordersendpoints (POST /api/v1/orders,/batch-create,PUT /api/v1/orders/{order_id},/batch-replace) and their gRPCOrdersServiceequivalents, and the MCPplace_limit_order/replace_ordertools. A maker-only guarantee, not a new order type: aLIMITorder usingGTC/GTDthat would cross the book is rejected instead of matching — REST400/ gRPCInvalidArgument, message containing"post-only order would cross"; batch responses carry a structurederror.code === "POST_ONLY_WOULD_CROSS"per item. Rejected onMARKETorders and when combined withIOC/FOK, and not persisted on the order. A singlereplaceOrdervalidates before cancelling the original (rejection leaves it resting);batchReplaceis cancel-first (a rejected item’s original is already cancelled). See Post-Only Orders
Monaco Protocol SDK v1.0.36
Added
- Registered deposit addresses with automatic sweeping —
sdk.sweeper.register({ clientId, userAddress })(POST /api/v1/sweeper/register; gRPCSweeperService.Register; Rust RESTregister_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 derivedsweeperAddressand aregistered/already_registeredstatus, with the background one-time initial balance check reported byinitialSweepCheck.@0xmonaco/contractsaddspredictSweeperAddress(...)plusSWEEPER_ABI/SWEEPER_FACTORY_ABIto derive the same address off-chain - Human-readable asset display names on trading pairs —
TradingPairgainsbaseAssetName/quoteAssetName(wirebase_asset_name/quote_asset_name) ongetPaginatedTradingPairs,getTradingPair, andgetTradingPairBySymbol(GET /api/v1/market/pairsand/{trading_pair_id}; gRPCMarketService.ListTradingPairs/GetTradingPair) — e.g."Robinhood Markets"forHOOD. Additive; use for name-based market search and display
Fixed
- MCP order tools flagged non-idempotent — the
place_limit_order,place_market_order, andreplace_ordertool 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_orderare 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_movementsnow paginate withpage/pageSize(not the ignoredlimit/offset) andget_trading_pairsgained acategoryfilter;place_limit_order/place_market_ordergainedmarginMode(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-derivedreadOnlyHint/destructiveHintannotations
Monaco Protocol SDK v1.0.34
Added
- Custom (vanity) PitPass TraderCodes — a wallet can claim a human-readable handle instead of the wallet-derived default:
setTraderCode(code)(PUT /api/v1/pitpass/codes/me/custom; gRPCTraderCodeService.SetTraderCode),clearTraderCode()(DELETE /api/v1/pitpass/codes/me/custom), and the public advisorycheckTraderCodeAvailability(code)(GET /api/v1/pitpass/codes/available).getMyTraderCode()gainscustomCode/isCustom/display; React addsuseMyTraderCode/useSetTraderCode/useClearTraderCode/useTraderCodeAvailabilityand MCP addsset_trader_code/clear_trader_code/check_trader_code_availability - Lifetime cost fields on terminal positions —
CLOSED/LIQUIDATED/EXPIREDrows fromgetPositions/getPosition(GET /api/v1/positions,/{positionId}; gRPCPositionsService.ListPositions/GetPosition) gainexitPrice(volume-weighted average exit),fundingPaid(lifetime funding, signed), andfeesPaid(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 —
ensureAuthenticatedis 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 with401s; 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:cryptowhen the global is missing, so@0xmonaco/coresession-key generation no longer throws on the first authenticated call when launched vianpx/nodeon Node 18
Monaco Protocol SDK v1.0.33
Breaking
- Authenticated MCP sessions are scoped by a capability profile —
MONACO_CAPABILITY(read|market-maker|full, defaultread) bounds which authenticated tools the MCP server registers; a private key no longer unlocks the full tool set. Order placement/cancel/replace requiremarket-maker; fund-movement and privilege tools requirefullor an explicitMONACO_TOOL_ALLOWLISTentry.MONACO_NETWORKis now required when a private key is set (no more silentmainnetdefault), andMONACO_TOOL_ALLOWLIST/MONACO_TOOL_DENYLISTgive 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. thesimulate_parent_margin_order_risk/simulate_risk_bucket_order_riskpreviews),get_user_trades,get_funding_payments,get_pending_withdrawals, and the publicget_pulsefeed. Reads areread-profile;close_positionneedsmarket-makerand the collateral-movement tools needfull
Changed
- Batch order replacement is cancel-first —
sdk.trading.batchReplace/batchReplaceMarginLimitOrders(and Rust REST/gRPCbatch_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-AfterHTTP-date parsing onAPIError—APIError.retryAfternow also parses the HTTP-date form of the header into remaining delay in seconds (past dates clamp to0); all three RFC 7231 formats are accepted and malformed/impossible dates are ignored. The value is in seconds — multiply by1000forsetTimeoutrequest_faucetMCP tool — fixed a dead tool that referenced removedaccessTokenwiring and always failed; it now callssdk.faucet.mintwith a Cloudflare TurnstileturnstileTokenand stays network-restricted (never onmainnet)- 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-keyAuthenticatehandshake (tokenless v0.8.8 model). Documentation-only; behavior and signatures unchanged
Monaco Protocol SDK v1.0.32
Added
- Full-history iterators —
sdk.trading.iterateOrders(params?)andsdk.profile.iterateUserTrades(params?)async generators walk full order and trade history (hot and archived), managing thepageTokencursor for you and yielding one row at a time - Multi-status order filter —
getPaginatedOrdersstatusaccepts an array (OrderStatusFilter, e.g.["SUBMITTED", "PARTIALLY_FILLED"]) sent as one request; a single status string still works - Completed position and funding types —
PositionStatusadds terminalLIQUIDATED/EXPIREDplus a newPositionStatusFilter;FundingRecordnow 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) andgetUserTrades(GET /api/v1/accounts/trades) — omittingpage/pageTokenstarts a full-history cursor walk. Legacy offset pagination is deprecated (thepageparam, andtotal/page/totalPages, which read0in cursor mode);pageSizeallows 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/useProfilefetch via cursor (returned data unchanged, totals now0); MCPget_ordersgainspageToken(pagedeprecated) - Mainnet WebSocket ingress — the
mainnetpreset now useswss://ws.apimonaco.xyz/ws(the previouswss://api.monaco.xyz/wsnever resolved; customwsUrloverrides are unaffected)
Fixed
- Spot equity marks at the live trade print — portfolio
spotEquity/totalEquityand 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(gRPCSimulateRiskBucketOrderRisk) no longer returns400/INVALID_ARGUMENTfor a first-useCROSSrisk bucket; it previews the deterministic cross bucket virtually, funded from the parent margin account’s unallocated collateral
Monaco Protocol SDK v1.0.29
Added
- Market pulse feed — new
sdk.pulse.getPulse()(GET /api/v1/pulse, public) returns a cached news/sentiment feed:generatedAt(also the ETag),watchlist, anditems[](asset or"MARKET",sentiment,summary,updatedAt,sources[]). Hourly refresh withETag/If-None-Match304and503until warm. Also gRPCPulseService.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, withorderBy,page/pageSize, andstartTime/endTime. Also gRPCMarketService.ListAllFundingHistory - Cursor pagination for orders and trades —
getPaginatedOrdersandgetUserTradesaccept apageTokenand returnnextPageToken: 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 —
FundingRecordgains acarriedflag;listFundingHistoryacceptsorderBy;ScreenerItemgainslatestFundingRateandlastFundingTime(nullfor spot pairs and perps with no settled window yet) - Terminal position lifetime figures — terminal rows (
CLOSED/LIQUIDATED/EXPIRED) report lifetimesizeandisolatedMargin, plus optionalnetRealizedPnlandrealizedRoe
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).
slippageToleranceBpsonly tightens the band, and the fee-simulation default market buffer follows to 1,000 bps - Entry-attached TP/SL re-enabled —
takeProfit/stopLossonplaceLimitOrder/placeMarketOrderare accepted again for margin entry orders (direction followsside); 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().closedTradesadds apositionstotal andgetPortfolioStats().winLossRatiois position-based;countstays fill-based - Batch market orders reject
timeInForce— matching the single-order endpoint; the SDK rejects it client-side before sending - Staging WebSocket ingress — the
stagingpreset now useswss://ws-staging.apimonaco.xyz/ws; the previous path still works during migration - Trade settlement status — the trade-level
settlementStatus(REST/gRPC wire) retiresSETTLED_ON_CHAIN; valid values arePENDING,SETTLED(terminal), andFAILED. The orderOrderStatusenum is unchanged - Scoped maintenance mode — during maintenance, mutating operations are rejected with REST
503OPERATIONS_BLOCKED(gRPCUNAVAILABLE+x-maintenance-blocked) while sign-in, order cancels, position closes, and risk simulations stay available
Fixed
- Retryable
503on matching-engine unavailability —close,tp-sl,pitpass/rewards/transfer, and the unauthenticated orderbook route return a retryable503instead of500when the engine is momentarily unreachable; non-transient faults still return500
Monaco Protocol SDK v1.0.26
Added
- Faucet quota introspection — new
sdk.faucet.getInfo()(GET /api/v1/faucet/info, session-authenticated) returns the wallet’sremainingRequests24h,dailyLimit, optionalnextAvailableAt, and up to five newest-firstrecentRequests. Wallet-scoped across apps, with the same rolling-window accounting asmint()enforcement; call it before requesting to avoid a429. A matchingget_faucet_infoMCP 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-legclosePositionflag (resolves the full live position size; omitquantity) and a request-levelocoflag (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);maxPositionNotionalis now a deprecated alias - New SDK options — optional
pollingIntervaloncreateMonacoSDK, andmaxReconnectAttempts/reconnectBaseDelayMs/connectionTimeoutMs/onResynconcreateMonacoWebSocket
Changed
- One-way futures order semantics —
sideis the authoritative direction;positionSideis optional and deprecated (must agree withsidewhen 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.
slippageToleranceBpscan 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
maxReconnectAttemptsfor a finite cap),onStatusChangereports"reconnecting", the client answers a serverPingwith aPong, and a newonResynccallback fires after each reconnect (slowClient: trueon close code1013) 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 theorderssnapshot frame — merging with>=(see Reconnect and resync) - Faster receipt polling — the internal viem client polls every 750ms by default (was 4000ms), speeding
vault.depositandwaitForTransaction; setpollingInterval: 4000to 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
developmentpreset now useswss://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, andtotalPnlbehindgetPortfolioChartfor 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 returns409 Conflict/ gRPCalready_existsinstead of a500that leaked Postgres detail; generate a fresh session keypair per session - Hardened error envelopes — database failures return a generic
500“Database error” and malformed JSON returns400“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 with400instead of a misleading404; valid lookups are unchanged
Monaco Protocol SDK v1.0.24
Fixed
- Isolated-margin partial-fill collateral returns to the parent account — cancelling (or IOC-releasing) the unfilled remainder of a partially filled isolated-margin limit order now returns the excess auto-funded risk-bucket collateral to the parent margin account’s free collateral immediately. Previously the full order-sized allocation stayed locked in the bucket until the position closed, inflating bucket equity and deferring liquidation beyond the isolated margin
reducePositionMarginrejects below-maintenance reductions —POST /api/v1/positions/{positionId}/margin/reduce(gRPCPositionsService.ReducePositionMargin) now returns400/INVALID_ARGUMENTwhen 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 leveragenewIsolatedMarginreports post-transfer position margin — thenewIsolatedMarginfield in the add/reduce position-margin responses now reflects the position’s margin after the transfer instead of the risk bucket’s allocated principal, which could include unrelated bucket equity
Monaco Protocol SDK v1.0.21
Added
- Portfolio analytics API — new
sdk.portfolionamespace withgetSummary,getPerformance,getRealizedPnl,getAssets({ period }of1d/1w/1m/all, defaultall), and current-onlygetMargin(). Backed by authenticatedGET /api/v1/accounts/me/portfolio/*and gRPCAccountsService.GetPortfolio*, returning authoritative Core calculations — transfer-neutral sampled TWR, explicit history coverage, spot/perp/funding/fee realized components, asset-ID-priced holdings, and separately scoped parent/risk-bucket margin. See the Portfolio Analytics guide
Removed
- Standalone backend authentication (breaking) —
POST /api/v1/auth/backend(authenticate_backend), gRPCAuthService.BackendAuth, and theBackendAuthRequest/BackendAuthResponsemessages are removed from the wire contract and generated SDKs. Backend applications continue to authenticate each request with theirsk_secret key in thex-server-keyheader — set it once withsdk.setServerKey("sk_..."). The TypeScriptauthenticateBackend()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 sourcespnlBreakdown.feesandtotalPnlfrom the authoritative trade-ledger fee total (same source asfeesPaid), 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 headlinepnlwas already correct and is unchanged - Portfolio chart fee metrics —
getPortfolioChartreads 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 theapplication_taker_feecomponent. This corrects the chart’sfees,realizedPnl, andtotalPnlseries for data recorded after the fix - SDK sends an explicit User-Agent header —
@0xmonaco/corenow sets aUser-Agentheader on every REST request. Monaco’s edge rejects an empty or absentUser-Agent, so SDK traffic could intermittently be blocked; the header is ignored in browsers, where it is forbidden
Monaco Protocol SDK v1.0.16
Added
- Exact tier fee rates on fee simulation —
fees.simulateFees(GET /api/v1/fees/simulate, gRPCFeesService.SimulateFees) now returnsmonacoTakerFeeBpsExactandmonacoMakerRebateBpsExact— decimal basis-points strings carrying the tier-resolved rate the ledger actually charges (e.g."6.5", negative for a rebate), equal tomonacoTakerFee / 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 optionalturnstileToken(REST body field / gRPCMintTokensRequest.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 return500 - Rust REST SDK builds on native-tls — the published
monaco-sdkcrate now builds itsreqwestclient 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/monacoMakerRebateBpson the fee-simulation response, plus the flatmakerFeeBps/takerFeeBpson 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, orGET /api/v1/fees/tier/fees.getMyFeeTier, for the caller’s real rate
Monaco Protocol SDK v1.0.7
Changed
faucet.mint()now confirms on-chain before resolving — previously resolved with a partial-success body inspected viaminted/failed. It now resolves only when every faucet transaction confirms on-chain, and throws otherwise:429when the daily quota is exhausted,502when mints failed or only partially confirmed (the error carries the minted/failed breakdown; delivered legs still consume the quota), and503when the faucet is temporarily unavailable (safe to retry). Move partial/failure handling into atry/catch; on a successful resolvefailedis empty and theMintTokensResponseshape is unchanged
Fixed
- Balances re-fetch is consistent after a live update — re-fetching balances immediately after a
balancesWebSocket event (sdk.profile.getUserBalances(),sdk.profile.getUserBalanceByAssetId(), orGET /api/v1/accounts/balances/ gRPCAccountsService.GetBalances) now reflects that update instead of occasionally returning stale data; no method arguments or response fields changed
Monaco Protocol SDK v1.0.3
Added
- Closed position execution history —
positions.listPositionHistoryaccepts a newreductionOnlyboolean to filter down to reductions, closes, and liquidations only. Each reduction row now includes backend-computed close facts:entryPrice,allocatedInitialMargin,fundingPaid,netRealizedPnl,realizedRoe, andpositionSide— no mark-price approximation needed for realized return display
Changed
- Conditional cross liquidation prices —
listPositions,getPosition, andgetPositionRisknow returnliquidationPricefor 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 returnfreeCollateralAfterand optionalestimatedLiquidationPrice?; all three accept an optionalreduceOnlyboolean; cross simulation requiresselectedTradingPairIds
Fixed
vault.withdrawwithautoWait: falseblocked 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; callretryWithdrawal(withdrawalIndex)once the proof is confirmedfees.getMyFeeTiervolume fields now in whole USD —weightedVolume14d,spotVolume14d, andperpVolume14dpreviously returned raw micro-USDC values (10⁶× too large), causing every user to resolve to a higher fee tier than warranted; all three fields now report whole-USD amounts
Monaco Protocol SDK v1.0.2
Added
- PitPass TraderCodes — every Monaco wallet now has a unique referral code. Share it to earn a share of the net protocol fee generated by referred users’ trades — up to three levels deep (30% / 15% / 5%). New SDK surface:
sdk.pitpass.getMyTraderCode(),getTraderCodeInfo(code),getRewardsBalance(),transferRewards({ token, amount }).sdk.auth.authenticate()accepts an optionalreferralCodeargument 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 byGET /api/v1/fees/tier(sdk.fees.getMyFeeTier) - Fee treasury — net protocol fees credited to a dedicated treasury at fill time; funds PitPass rewards without minting new tokens
Monaco Protocol SDK v0.8.22
Added
- Quantity step size on trading pairs —
TradingPair.quantityStepSize(string) is now returned bymarket.getTradingPair,market.getPaginatedTradingPairs, andGET /api/v1/market/pairs; the base-token lot-size increment used to validate order quantity precision - Unrealized PnL and breakdown —
getPortfolioStatsgainsunrealizedPnl,totalPnl, andpnlBreakdown(spotRealized, spotUnrealized, perpsRealized, perpsUnrealized, fundingPaid, fees);getPortfolioChartaccepts six new metrics:unrealized_pnl,total_pnl,realized_pnl,funding_paid,fees,equity - Position PnL history — new
GET /api/v1/positions/{positionId}/pnl/historyendpoint (positions.getPositionPnlHistory(id, { interval, startTime?, endTime? }),usePositionPnlHistoryhook, gRPCPositionsService.GetPositionPnlHistory) returns bucketed PnL samples on the 1m/5m/15m/1h/4h/1d interval ladder
Fixed
- Per-position margin requirements —
listPositions,getPosition, andgetPositionRisknow return position-scopedinitialMarginRequiredandmaintenanceMarginRequiredinstead of the parent margin-account aggregate; no field-shape changes
Monaco Protocol SDK v0.8.21
Added
- Get conditional order by ID — new
GET /api/v1/orders/conditional/{conditionalOrderId}endpoint (sdk.trading.getConditionalOrder(id),useTrade().getConditionalOrder(id), gRPCOrdersService.GetConditionalOrder) fetches a single TP/SL conditional order by its UUID — the same IDs returned astakeProfitOrderIdandstopLossOrderIdin thecreateOrderresponse
Changed
- Realized PnL accounting semantics —
getPortfolioStats(pnl) andgetPortfolioChart("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, andtakerTotalPaymentnow 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 —
simulateRiskBucketOrderRiskwithmarginMode: "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/ordersand batch-create for a parent margin-account order that omitsmarginModeand no isolated risk-bucket selector now return a client error instead of silently routing through the isolated path; passmarginMode: "CROSS"ormarginMode: "ISOLATED"explicitly
Security
conditional_ordersWebSocket now requires auth at subscribe time — thesdk.ws.conditionalOrderschannel now gates at subscribe (not just delivery); unauthenticated subscribe attempts are rejected
Monaco Protocol SDK v0.8.20
Changed
- Cross margin auto-resolved on order placement —
POST /ordersandPOST /orders/batch-createwithmarginMode: "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 requestedtradingPairId, 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}/limitsfor 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
balancesWebSocket missing spot-fill updates —sdk.ws.balances()now emits atrade-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 tradingsimulateFeesused flat fee rate instead of your tier rate —sdk.trading.simulateFeesnow 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, andgetAvailableCollateralnow reflect the correctfreeCollateralright after the replace; previously the bucket’s allocated collateral stayed unchanged until the order was cancelled or filled
Monaco Protocol SDK v0.8.19
Added
- Cross margin on risk buckets — margin orders can now target a shared cross risk bucket spanning several pairs, not just per-pair isolated buckets.
placeLimitOrder/placeMarketOrder/batchCreateaccept an optionalmarginMode("ISOLATED"|"CROSS", defaultISOLATED);"CROSS"requiresmarginAccountIdand routes to the active cross bucket for the pair.Positionreads exposemarginMode, andMarginAccountSummaryexposesselectedTradingPairIdson cross rows.transferCollateralToRiskBucketandsimulateRiskBucketOrderRiskacceptmarginMode/selectedTradingPairIdsand echo the resolved bucket. Additive —marginModedefaults toISOLATED, shapes unchanged
Monaco Protocol SDK v0.8.18
Fixed
spotEquitymark-priced —getPortfolioStats().spotEquity(andtotalEquity, 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, MCPget_my_fee_tier) returns your resolved fee tier (1–6), rolling 14-day weighted/spot/perp volumes, the volume still needed to reach the next tier, and a trading pair’s six-row maker/taker fee schedule
Monaco Protocol SDK v0.8.16
Fixed
- Portfolio realized PnL —
getPortfolioStats().pnlandgetPortfolioChart(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 winLossRatiotype —PortfolioStats.winLossRatiois nownumber | null(wasstring), matching the API;nullwhen the period has no closed trades
Added
- Spot / perps equity breakdown —
PortfolioStatsgainsspotEquityandperpsEquity(quote-normalized strings) alongsidetotalEquity
Changed
totalEquityincludes perpetuals — now spot balances plus perps (margin) account equity, not spot alonemaxDrawdownnullable —PortfolioStats.maxDrawdownis nowstring | null(nullwhen no closed trades)
Monaco Protocol SDK v0.8.15
Breaking
- REST wire format is now camelCase — every REST request/response JSON field and path parameter switches from snake_case to camelCase (
trading_pair_id→tradingPairId,created_at→createdAt,page_size→pageSize,/withdrawals/{withdrawal_index}→/withdrawals/{withdrawalIndex}, …). The TypeScript SDK already speaks camelCase, so SDK consumers just bump the version; direct REST callers must rename fields and path params. The WebSocket raw wire and gRPC are unchanged
Added
- List pending withdrawals —
sdk.withdrawals.listPendingWithdrawals({ page?, pageSize? })(GET /api/v1/withdrawals) returns the caller’s pending (awaiting-proof) withdrawals, newest first and paginated; eachPendingWithdrawalcarrieswithdrawalIndex,assetId,assetSymbol,amount,destination,status,createdAt. Also in@0xmonaco/reactviauseVault().listPendingWithdrawals
Monaco Protocol SDK v0.8.14
Removed
- Idless “auto margin account” endpoints removed —
transferCollateralToAutoMarginAccount(POST /api/v1/margin/collateral/transfer-in) andsimulateAutoMarginOrderRisk(POST /api/v1/margin/simulate-order-risk) now 404. UsetransferCollateralToParentMarginAccount/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-pairtransferCollateralToRiskBucketandsimulateRiskBucketOrderRisk - 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/reactand thebatch_close_all_positionsMCP tool - Raw total balance — balance responses gain
total_balance_raw(raw integer string,available + locked) alongside normalizedtotal_balance fxasset class — Pyth-backed FX perps addcategory: "fx"to trading-pair filters and responses
Changed
margin_bucket_*→risk_bucket_*rename (breaking for REST/SDK) — order optionsmarginBucketId/marginBucketCollateral→riskBucketId/riskBucketCollateral, and thePositionresponse fieldmargin_bucket_id→risk_bucket_id. gRPC field numbers unchanged; JSON keys change- Withdrawals migrated to merkle-proof
executeWithdrawal(signed path dropped) —withdraw/retryWithdrawalsignatures 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 viaretryWithdrawal);WithdrawResult.statusadds"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 typed0x${string}instead ofstring. Type-only; inputs unchanged
Monaco Protocol SDK v0.8.11
Removed
CreateConditionalOrdergRPC RPC removed — the method had returnedFAILED_PRECONDITIONsince it was deprecated in v0.8.7. Attach TP/SL atCreateOrderor useAttachPositionTpSl/sdk.positions.attachPositionTpSl(...)
Added
- Asset-class
categoryon trading pairs —TradingPairandScreenerItemgaincategory: "crypto" | "equities" | "commodities";getPaginatedTradingPairsandgetScreeneraccept an optionalcategoryfilter - Life-to-date market stats —
sdk.market.getMarketStats()returns exchange-wide cumulativetotal_quote_volume_ltd/total_trade_count_ltd;MarketMetadataandScreenerItemgain 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/Fromnow stay scoped to the parent whentradingPairIdis omitted, even when an active cross-risk bucket exists getAvailableCollateralnow correctly surfaces parent margin account free collateral
Monaco Protocol SDK v0.8.10
Breaking Changes
- Margin account provisioning is now implicit:
POST /api/v1/margin/accountsand thecreateMarginAccount/ensureParentMarginAccountmethods are removed. The parent margin account is created automatically on the first accountless parent collateral transfer-in — usesdk.marginAccounts.transferCollateralToParentMarginAccount(...) - Standalone conditional-order creation removed:
createConditionalOrder(core + React) andPOST /api/v1/orders/conditionalare gone. Attach TP/SL at order placement or usesdk.positions.attachPositionTpSl(...). Listing/cancelling conditional orders is unchanged GET /api/v1/trades/userremoved from REST (now gRPC-only); useGET /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.withdrawstays 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/GetPortfolioChartare now implemented - Generated
@0xmonaco/types/wirenamespace; 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).
GetMarketMetadatano longer returns livemark_price/index_price(usegetMarkPrice/getIndexPrice) createDelegatedSessionnow requiressessionPublicKeyand its response no longer includesaccess_tokenexecuted_aton user trades now uses an explicit+00:00offset instead ofZ(any ISO 8601 parser is unaffected)
Monaco Protocol SDK v0.8.8
Breaking Changes
- Authentication moves to ed25519 session-key signing: JWT access/refresh tokens are replaced by wallet-authorized session keys.
AuthStatenow carriessessionPublicKey/sessionPrivateKeyinstead ofaccessToken/refreshToken;sdk.auth.refreshToken/revokeTokenbecomerefreshSession/revokeSession(usesdk.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:
SDKConfiggains optionalapiUrl/wsUrloverrides for self-hosted or CI/e2e stacks;networkstill selects the chain
Monaco Protocol SDK v0.8.7
Fixes
withdrawable_collateralnow reflects live free collateral:MarginAccountSummary.withdrawable_collateraland thenew_withdrawable_collateralfield on transfer-in/out responses return the risk-bounded free collateral amount instead of equity-basedavailable_margin. Safe to pre-fill into a “Max withdraw” button. AffectsgetMarginAccountSummary,listMarginAccounts,transferCollateralToMarginAccount,transferCollateralFromMarginAccount- Transfer-out and isolated margin reduction use margin state directly:
transferCollateralFromMarginAccountandsdk.positions.reducePositionMarginno 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).OpenInterestalso gains an optionalopen_interest?: stringbase-units alias for backward compat
Changes
latest_ordersandlatest_movementsalwaysundefined: With Redis removed from the orders/movements path, the optional Redis hot-cache shadow fields ongetOrders()andgetUserMovements()are no longer populated. Both remain on the response interface as optional — switch to the paginatedorders/movementsarrays
Monaco Protocol SDK v0.8.5
New Features
- App Statistics (
sdk.applications.getApplicationStats): Backend-auth only. Returns aggregatevolume,maker_fee,taker_fee,application_taker_fee, andtrade_countfor trades where the application’s users were the taker. Optionalsinceparam (ISO 8601) for time-ranged queries. Also available asApplicationsService/GetAppStatsvia gRPC transferCollateralToRiskBucket: Fund the auto-resolved risk bucket for a(tradingPairId, strategyKey)pair — nomarginAccountIdlookup requiredsimulateRiskBucketOrderRisk: Preflight a perp order against the auto-resolved risk bucket for a(tradingPairId, strategyKey)pairlistMarginAccountsfilter: New optionaltradingPairIdparam to filter accounts by trading pairMarginAccountSummaryfields:trading_pair_idandstrategy_keynow returned in list and summary responses
Monaco Protocol SDK v0.8.4
New Features
- Delegated Agents (
sdk.delegatedAgents): Authorize a separate signer wallet to submit orders on behalf of an owner account. The owner retains full custody; the agent is constrained to the trading actions, pairs, order types, leverage, and notional limits defined in its policy upsertDelegatedAgent: Register or update an agent policy — allowed actions, trading pairs, order types, time-in-force, max leverage, max order notional, expirylistDelegatedAgents: List all active delegated agents for the authenticated ownerrevokeDelegatedAgent(agentId): Revoke an agent policy; future policy-gated trading fails even if a delegated auth session has not expiredcreateDelegatedSession({ ownerUserId }): Agent exchanges its own authenticated session for a short-lived, owner-scoped delegated session — orders then settle against the owner’s margin accountsstrategyKeyon order placement: Optional field onplaceLimitOrder,placeMarketOrder,batchCreate, andbatchReplace. 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
Monaco Protocol SDK v0.7.9
New Features
- Parent TP/SL at order placement: Pass
takeProfitand/orstopLossoptions directly onplaceLimitOrder/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 placementPENDING_PARENTconditional order state: New state for legs waiting on an unfilled parent order. Cancelling the parent auto-cancels pending childrenConditionalOrderfields:parent_order_id,association_type,linked_group_id,activated_atadded to list and websocket responses
Changes
conditional_order_updatewebsocket event: New fieldsparentOrderId,associationType,linkedGroupId,activatedAt; new reason valuesactivated,parent_cancelled,oco_cancelledcreateConditionalOrderdeprecated for TP/SL: Use parent TP/SL on order placement orsdk.positions.attachPositionTpSlfor existing positions
Monaco Protocol SDK v0.7.8
New Features
sdk.vault.retryWithdrawal(withdrawalIndex): New method to re-submit a withdrawal whose on-chain transaction failed or was dropped, without allocating a new withdrawal slot
Changes
sdk.vault.withdraw()return type: Now returnsWithdrawResult(extendsTransactionResultwithwithdrawalIndex: number). StorewithdrawalIndex— required to retry a failed submissionposition_idrejected in position request bodies: The API gateway now rejectsposition_idin the JSON body ofPOST /positions/{id}/close,POST /positions/{id}/add-margin, andPOST /positions/{id}/reduce-margin. The field is path-only. SDK callers are unaffected — raw HTTP callers must remove it from the request body
Monaco Protocol SDK v0.7.2
New Features
- Vault deposits upgraded: Vault contract now embeds the application’s
client_idin 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.reasonnow includes"margin_transfer_in"and"margin_transfer_out" TransferCollateralResponseexpanded: Newnew_total_collateral_valueandnew_withdrawable_collateralfields — update collateral UI without a follow-up fetchuseUserBalances(React): Spot events preserve margin-collateral component; margin-transfer events replace the full total
Monaco Protocol SDK v0.7.0
New Features
- Perpetuals: First-class perp surface —
sdk.marginAccountsandsdk.positionsmodules, perp helpers onsdk.market, perp options (marginAccountId,positionSide,leverage,reduceOnly) onplaceLimitOrder/placeMarketOrder - Conditional orders: Standalone
sdk.trading.createConditionalOrderplusattachPositionTpSlfor TP/SL on existing positions - WebSocket: New
conditional_order_updateevent streams TP/SL lifecycle — subscribe viasdk.ws.conditionalOrders(handler, tradingPairId?) - React: New
usePositionshook;useMarketanduseTradeextended with perp helpers
Breaking Changes
- Pagination standardized to
page/page_size:limit,offset, andskipremoved across all paginated endpoints;total_countrenamed tototal - Trading pair fields renamed:
Order.trading_pair→.trading_pair_id,ProfileOrder.trading_pair→.symbol, WebSocket order eventstradingPair→symbol - Flattened response shapes:
datawrapper removed from trades and market responses (e.g.,getPaginatedTradingPairsnow returnsresponse.trading_pairsdirectly) - Removed
successfield from batch order, trading-pair, and candlestick response types — use HTTP status ortotal_failed === 0 - Close-position requests:
position_idonly accepted in URL path, not body
Monaco Protocol SDK v0.6.3
Bug Fixes
- MCP server: Added
resetSDKfor test isolation. No user-facing changes
Monaco Protocol SDK v0.6.2
Bug Fixes
revokeToken()parameter removed:sdk.auth.revokeToken()no longer accepts arefreshTokenargument. The server identifies the session from the access token in theAuthorizationheader — passing a body was causing a silent400error, meaningsdk.logout()was silently failing. Remove the argument from any directrevokeToken()calls;sdk.logout()requires no changes- MCP server: Network config is now passed explicitly to
registerTools, fixing flaky CI tests from parallelprocess.envmutations. No user-facing changes
Monaco Protocol SDK v0.6.1
Simulate Fees Improvements
Improves fee simulation to support the 100% balance slider and accurate fee estimation for market orders. New Features:max_quantity: Response now includes the maximum quantity the authenticated user can afford at the given price, after fees — use this directly for the 100% slider instead of computingbalance / priceorder_type=MARKET: Passorder_type: "MARKET"to get a slippage-adjustedbuy_order_lock_amountthat accounts for worst-case execution costslippage_tolerance_bps: Configurable slippage tolerance (0–1000 bps, default 500). Only valid withorder_type: "MARKET"
@0xmonaco/types: SimulateFeeParams (new order_type, slippage_tolerance_bps), SimulateFeeResponse (new max_quantity, max_quantity_raw, slippage_tolerance_bps)
No breaking changes.
Read full release notes →
Monaco Protocol SDK v0.6.0
Portfolio Analytics API
This release adds portfolio analytics to the TypeScript SDK — aggregate trading statistics and time-series chart data for the authenticated user. New Features:getPortfolioStats(period): Fetch aggregate stats (PnL, volume, fees, win/loss ratio, max drawdown, total equity) scoped to24h,7d,30d, orallgetPortfolioChart(period, metric): Fetch bucketed time-series data forvolumeorpnlover a given periodgetUserTrades(params?): Fetch paginated trade history with optional filtering by trading pair- Fee Rates on Profile:
getProfile()now returnstaker_fee_bpsandmaker_fee_bpsonUserProfile
@0xmonaco/types: PortfolioPeriod, PortfolioMetric, PortfolioStats, PortfolioChartPoint, PortfolioChartResponse, UserTrade, GetUserTradesParams, GetUserTradesResponse
No breaking changes.
Monaco Protocol SDK v0.5.9
Movements API Cleanup & Order Status Update
This release removes the unsupportedsource filter from movements APIs and adds SETTLED_ON_CHAIN as a terminal order status.
Breaking Changes:
sourceRemoved from Movements APIs:getPaginatedUserMovements()anduseUserMovements()no longer acceptsource. It was never supported by the API and caused a 400 error if passed. Removesourcefrom any calls that use it — other filters (entry_type,transaction_type,asset_id) are unaffected
SETTLED_ON_CHAINOrder Status: Added as a terminalOrderStatusvalue representing a filled order fully settled on-chain. Full lifecycle:SUBMITTED → PARTIALLY_FILLED → FILLED → SETTLED → SETTLED_ON_CHAIN
Monaco Protocol SDK v0.5.8
Profile Endpoint Simplification & Market Order Cleanup
This release removes extraneous fields from the/me profile endpoint and eliminates timeInForce from market orders.
Breaking Changes:
/meResponse Simplified:GET /api/v1/accounts/menow returns only core profile metadata.balances,recent_movements, andrecent_ordersare no longer included — use/balances,/movements, and/ordersdirectlytimeInForceRemoved from Market Orders: The API rejects market orders that includetime_in_force. Removed from@0xmonaco/types,@0xmonaco/core, and@0xmonaco/react. Limit orders are unaffected
@0xmonaco/react):
useProfile()now exposesbalances,movements,orders, andloadingas top-level state, populated in parallel byfetchProfile()- New
getPaginatedOrdersaction onuseProfile()for manual order pagination
Monaco Protocol SDK v0.5.7
Batch Orders, MCP Server & Network Configuration Improvements
This release introduces batch order creation and replacement for high-frequency trading workflows, a fully rebuilt MCP server for AI assistant integrations, removes the manual WebSocket URL configuration requirement, and tightens thenetwork 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()anduseUserMovements()now support server-side filtering byentry_type,transaction_type, andasset_id
wsUrlRemoved: ThewsUrloption has been removed fromSDKConfigandMonacoProvider. WebSocket URL is now automatically resolved from thenetworkparameterNetworkType Tightened: Only preset network names are accepted. Passing a custom URL string now throwsInvalidConfigErroruseUserMovementsSignature Changed: Now accepts an options object —useUserMovements({ maxMovements: 50 })instead ofuseUserMovements(50)
Monaco Protocol SDK v0.5.6
Real-Time Features & API Improvements
This release introduces real-time balance tracking via WebSocket, refines the batch cancel API with two dedicated methods, and improves vault error handling. New Features:- Real-Time Balance Updates: New
sdk.ws.balances()WebSocket channel for live balance updates triggered by deposits, withdrawals, trades, and order locks - useUserBalances Hook: React hook combining REST + WebSocket for real-time balance tracking with manual refresh capability
- Batch Cancel Split:
batchCancelOrders()split intobatchCancel(orderIds)andbatchCancelAll(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
- 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()andbatchCancelAll()methods for clearer intent
- Improved vault error handling with clear
InvalidConfigErrormessages when no wallet account is connected
Monaco Protocol SDK v0.5.5
Batch Cancel Orders & Enhanced React Hooks
This release introduces batch order cancellation, new React hooks for real-time data, and simplifies order statuses by removing deprecated intermediate states. New Features:- Batch Cancel Orders: Cancel multiple orders in a single API call with
sdk.trading.batchCancelOrders() - Cancel All Orders: Use
{ cancelAll: true }to cancel all active orders instantly - useUserOrders Hook: Real-time order tracking via WebSocket with automatic state management
- useOHLCV Hook: Live candlestick data for charting with WebSocket updates
- Order Status Simplification:
PENDINGandACKNOWLEDGEDstatuses removed - orders now transition directly toSUBMITTED - vault.getBalance() Deprecated: This method no longer functions - use
profile.getUserBalanceByAssetId()orprofile.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, andLedgerMovementtypes (see full release notes for details)
Monaco Protocol SDK v0.5.4
Real-Time User Movements & Trade Feed Improvements
This release introduces real-time user movement events via WebSocket and refactors theuseTradeFeed 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:
useTradeFeednow shares WebSocket subscriptions across components
useTradeFeedno longer acceptsmaxTradesparameter (fixed at 50 trades)useTradeFeedno longer returnsclearErrorfunction
- Fixed self-referential import in
useOrderbookhook - Fixed circular dependency between provider and hooks modules
- WebSocket now supports multiple handlers per channel
Monaco Protocol SDK v0.5.3
Candlestick API Refactor & Market Metadata
This release refactors the candlestick API to use trading pair UUIDs and introduces TradingView-compliant query patterns. Breaking Changes:getCandlesticksnow requirestradingPairId(UUID) instead ofsymbol- Method signature changed from
(symbol, interval, startTime, endTime)to(tradingPairId, interval, params?)
- Optional
startTime,endTime, andlimitparameters viaGetCandlesticksParams - 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
Monaco Protocol v0.5.2
gRPC API & Testnet Faucet
This release introduces the Monaco gRPC API for high-performance trading and a new testnet faucet endpoint. gRPC API:- Full trading support: create, cancel, and replace orders via gRPC
- Real-time market data: orderbook snapshots, recent trades, and OHLCV candles
- Account management: profile, balances, movements, and sub-account limits
- Binary Protocol Buffers serialization for reduced payload size
- HTTP/2 multiplexing for concurrent requests
- New
POST /api/v1/faucet/mintendpoint for authenticated users - Mints all testnet tokens (BTC, ETH, SOL, SEI, USDC, and stock tokens)
- Daily rate limit configurable via feature flags
Monaco Protocol v0.5.1
Architecture Evolution & Microservices
This release marks a significant evolution from a monolithic API to a distributed microservices architecture designed for high-frequency trading. Microservices Architecture:api-gateway-rust: REST API for trading, accounts, market data (HTTP/1.1, Port 8080)ws-api: Real-time orderbook & trade streaming (WebSocket, Port 8080)grpc-api: High-performance trading API (gRPC/HTTP2, Port 50051)matching-engine: Order matching & trade execution
- REST API:
https://{host}/ - WebSocket:
wss://{host}/ws - gRPC:
https://{host}over HTTP/2 with gRPC method paths
Monaco Protocol SDK v0.5.0
WebSocket API Refresh & Trades Endpoint
This release introduces a new Trades REST API, real-time trades WebSocket channel, and updates the WebSocket SDK to use a cleaner functional pattern. New Features:- Trades REST API:
GET /api/v1/trades/{trading_pair_id}for fetching trade history - Trades WebSocket channel:
trades:{trading_pair_id}for real-time trade events - Balance by asset endpoint now properly documented
- 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
wsUrlis now a required parameter in SDK initialization
- Complete WebSocket documentation rewrite with updated examples
- New Trades API reference documentation
- Fixed balance endpoint path parameter (
asset_idinstead oftoken)
Monaco Protocol SDK v0.4.1
SDK Updates & Infrastructure Improvements
This release includes SDK dependency updates and backend infrastructure improvements, migrating the indexer from deprecated ethers.rs to alloy, along with critical bug fixes. SDK Updates:- TypeScript 5.9+ with improved type safety and IntelliSense
- viem 2.31+ for enhanced blockchain interactions
- Optimized TypeScript configuration recommendations
- 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
- New repositories: ApplicationRepository, BlockchainEventsRepository, ProcessedBlocksRepository
- Improved RPC provider compatibility (no special filters required)
- Enhanced test coverage with new integration and unit tests
Monaco Protocol SDK v0.4.0
Custom RPC Configuration & Network Flexibility
This release introduces configurable RPC endpoints and custom network support, giving developers full control over their infrastructure. Key Features:- Required
seiRpcUrlparameter for SDK initialization - Custom RPC provider configuration to avoid rate limits
- Custom network configuration support
- Enhanced infrastructure control for production apps
seiRpcUrlis now required when initializing the SDK- Affects all TypeScript SDK and React MonacoProvider usage
Monaco Protocol SDK v0.3.1
Balance Management Enhancements
This release adds new SDK methods for querying user token balances with pagination and individual token lookups. Key Features:- Paginated balance listing with
getUserBalances() - Individual asset balance queries with
getUserBalanceByAssetId() - Enhanced balance details (available, locked, total)
Monaco Protocol v0.3.0
Orderbooks and Flat Fees
This release introduces major improvements to orderbook functionality, fee management, and order lifecycle handling with real-time orderbook snapshots, flat fee support, and comprehensive time-in-force options. Key Features:- Real-time orderbook snapshots via WebSocket with magnitude grouping
- Flat fee support and dedicated fee API endpoints
- Full Time-in-Force implementation (GTC, IOC, FOK)
- Average fill price tracking in order responses
- Enhanced order validation and rejection events
- Consistent uppercase enum formatting across entire API
Monaco Protocol SDK v0.2.0
Trading Enhancements & Real-time Data
This release introduces significant trading improvements including market order slippage protection, advanced limit order features, and real-time WebSocket data streaming. Key Features:- WebSocket real-time order events with 8 event types
- WebSocket real-time OHLCV candlestick data (6 timeframes: 1m, 5m, 15m, 1h, 4h, 1d)
- Automatic reconnection and connection health monitoring
- Slippage tolerance for market orders (0.1% to 10%)
- Good Till Cancel (GTC) with 90-day default expiration for limit orders
- Custom expiration dates for limit orders
- Enhanced order response with slippage metrics
Monaco Protocol SDK v0.1.6
Multi-Vault Architecture Support
The SDK now supports multiple vault contracts to align with the new backend architecture. Each client ID (app) now uses its own dedicated contract, providing better isolation and scalability. Key Features:- Multiple vault contract support
- Enhanced contract isolation
- New React package
Monaco Protocol SDK v0.1.5
General Release Update
Stable release with improved reliability and performance optimizations across all packages. Improvements:- Enhanced stability across the SDK
- Performance optimizations
- Bug fixes and reliability improvements
Monaco Protocol SDK v0.1.1
Initial Release
The first stable release of the Monaco Protocol SDK, providing developers with essential tools to integrate with the Monaco Protocol CLOB. Core Features:- Complete TypeScript SDK
- Authentication system
- Trading operations
- React integration
- Comprehensive API coverage

