Monaco Protocol SDK v1.0.75
This release moves the remaining page-number listings to cursor pagination, makes the application reporting endpoints public (selected byclientId, no server key), and lists book orders only from GET /api/v1/orders. It adds periodic position valuation frames, openedAt on positions, sub-account collateral transfers, isolated-only markets, a connecting WebSocket status with attempt context, and paged PitPass downline earnings that report every referral level. Action items: stop sending page and walk listings with pageToken / nextPageToken; pass clientId to the application listings and drop setServerKey; list TWAP and conditional orders with their own methods; page earningsBySource and read summary.byLevel; accept the new PRICE_BAND and SLIPPAGE_TOLERANCE terminal reasons; upgrade before you rely on valuation frames.
Breaking
Page-number pagination is removed
Thepage request field is gone from these listings, all of which now walk with pageToken: omit it (or send "") to start, then follow nextPageToken until it comes back empty.
- Positions:
sdk.positions.listPositions(GET /api/v1/positions; gRPCPositionsService.ListPositions) andlistPositionHistory(GET /api/v1/positions/history). - Margin accounts:
sdk.marginAccounts.listMarginAccounts(GET /api/v1/margin/accounts),getMarginAccountMovements(GET /api/v1/margin/accounts/{marginAccountId}/movements) andgetParentMarginAccountMovements(GET /api/v1/margin/parent-margin-account/movements). - Account reads:
sdk.profile.getPaginatedUserMovements(GET /api/v1/accounts/movements),getUserBalances(GET /api/v1/accounts/balances) andlistFundingPayments(GET /api/v1/accounts/funding-payments). - Funding history:
sdk.market.listFundingHistory(GET /api/v1/market/pairs/{tradingPairId}/funding/history) andlistAllFundingHistory(GET /api/v1/market/funding/history). - Application listings:
sdk.applications.listApplicationOrders,listApplicationUsers,listApplicationMovementsandlistApplicationBalances(GET /api/v1/applications/orders,/users,/movements,/balances).
page is rejected with 400. Over gRPC protobuf drops the unknown field, so an old client keeps working but reads the first cursor page on every call; remove page and upgrade your generated stubs. The TypeScript SDK rejects page on every listing above with a ValidationError before sending, so a caller that handles only 400 sees a client-side error instead.
pageSize accepts 1 to 1000 (was 1 to 100). Defaults are unchanged (20, or 50 for funding history). The funding, balances, margin-account and application listings now reject a larger value instead of clamping it. The page response field is gone everywhere. Counts change by listing:
totalis gone from position history, margin-account movements, and application orders and movements.- Positions, account movements, funding payments, funding history, application users and application balances keep
total(andtotalPageswhere they had it), bounded by a server count ceiling, with a newtotalCappedflag that istruewhen more rows matched. Do not drive a paging loop fromtotal. - Balances and margin accounts keep an exact
totalwith nototalCapped.
OPEN filter walks by margin account, trading pair and position id), and funding payments by (createdAt, id) descending. Funding history sorts by (settledAt, id), application users by user id and application balances by balance id. Balances and margin accounts follow a fixed key order. Rows with no recorded timestamp are no longer listed in margin-account movements, application movements and funding payments.
@0xmonaco/react’s useUserMovements drops its page option and does not take a cursor: it reads one newest-first page and keeps it current from the movements channel. Walk the full history with sdk.profile.getPaginatedUserMovements. See Positions, Profile and Margin Accounts.
Application reporting endpoints are public
listApplicationOrders, listApplicationUsers, listApplicationMovements, listApplicationBalances and getApplicationStats (GET /api/v1/applications/orders, /users, /movements, /balances, /stats; gRPC ApplicationsService) no longer use the x-server-key header, and application secret keys are no longer issued. Each call takes a required params with clientId, the public client ID you log in with. A missing clientId is 400, and an unknown or inactive one is 404. The five share a per-client-IP limit of 60 requests per minute (burst 120); over it REST answers 429 with Retry-After and gRPC answers RESOURCE_EXHAUSTED with a google.rpc.RetryInfo delay. The SDK removes setServerKey, and AppUser no longer carries email, canWithdraw or isBanned. See Applications.
GET /api/v1/orders lists book orders only
sdk.trading.getPaginatedOrders and iterateOrders (GET /api/v1/orders; gRPC OrdersService.ListOrders) return limit and market orders only, under every status and tradingMode filter, and total counts only those. Order.orderType is "LIMIT" | "MARKET" again: the ListedOrderType alias, its "TWAP" / "CONDITIONAL" members and the optional Order.twap / Order.conditional fields are removed. List TWAP parents with sdk.trading.listTwapOrders and TP/SL orders with sdk.trading.listConditionalOrders; both are unchanged. The request parameters and cursor contract are unchanged. See Trading.
PitPass downline earnings are paged
sdk.pitpass.getMyReferralDownline(params?) (GET /api/v1/pitpass/referrals/me/downline; gRPC TraderCodeService.GetMyReferralDownline; React useMyReferralDownline) pages earningsBySource: a call without paging returns the first 100 rows. Pass pageSize (1 to 500, default 100) and pageToken, and follow nextPageToken until it is empty. Rows are ordered by totalEarned descending; a wallet that traded through several applications is merged within a page and can appear again on a later page.
summary.byLevel lists { level, earned } for every level with earnings, across every source, on every page. summary.level1Earned, level2Earned and level3Earned are deprecated: they still report levels 1 to 3 and nothing deeper. getMyReferralPosition’s upline holds as many entries as PitPass currently rewards levels (at most 10), and getMyReferralTree expands nodes down to that depth. See PitPass.
New order terminal reasons
terminalReason gains PRICE_BAND (the protective price band stopped the order and its remainder was cancelled) and SLIPPAGE_TOLERANCE (the order’s own slippageToleranceBps was the tighter bound). Both used to end CANCELLED with no reason. Code that switches exhaustively on terminalReason must accept them.
Added
Position valuation frames
sdk.ws.positions(handler, tradingPairId?, onSnapshot?, onValuation?) takes an optional onValuation callback for the channel’s periodic position_valuation frames (PositionValuationEvent). Each frame carries positionId, marginAccountId, tradingPairId, markPrice, unrealizedPnl, liquidationPrice (null when it cannot be derived), initialMarginRequired, maintenanceMarginRequired and valuedAt, and no size, side, status or version. Apply a valuation only to a position you hold as OPEN whose updatedAt is earlier than valuedAt; ignore it otherwise. handler still receives position_update frames only, and without onValuation valuation frames are dropped silently. Earlier SDK versions log one parse error per valuation frame, so upgrade before you rely on them. See WebSockets.
openedAt on positions
Positions report the instant they opened: the fill that took exposure from zero to non-zero. Position.openedAt (string | null; null when unknown) is on listPositions, getPosition and the other position reads, and openedAt is on positions WebSocket events and snapshot rows (absent when the frame does not carry it). It never changes for a positionId; a flip opens a new position. Time held is now - openedAt while OPEN and updatedAt - openedAt once terminal. See Positions.
Sub-account collateral transfers
sdk.marginAccounts.transferCollateralToSubAccount(marginAccountId, amount) (POST /api/v1/margin/sub-accounts/{marginAccountId}/collateral/transfer-in; gRPC MarginAccountsService.TransferCollateralToSubAccount) moves collateral from the default margin account into a sub-account, and transferCollateralFromSubAccount (.../collateral/transfer-out; gRPC TransferCollateralFromSubAccount) moves it back. Both take a sub-account UUID and a positive decimal amount and resolve to a TransferMarginCollateralResponse; the default margin account itself is refused with 400. The MCP server adds transfer_collateral_to_sub_account and transfer_collateral_from_sub_account. See Margin Accounts.
Isolated-only markets
TradingPair.isolatedOnly (GET /api/v1/market/pairs, /market/pairs/{tradingPairId}) marks a market that accepts cross margin only to reduce a position. A CROSS order that is not reduce-only is rejected with 400 / INVALID_ARGUMENT and the error code CROSS_MARGIN_NOT_ALLOWED; open with marginMode: "ISOLATED" instead. A copy that would land in a cross risk bucket on such a market is skipped with MARKET_STATE and does not count toward the follower skip limit. See Trading.
connecting status and connection-attempt context
onStatusChange now reports "connecting" when a connection opens outside a retry cycle (the constructor’s auto-connect, a reconnect after terminal authentication loss, and a connect() after a terminal "disconnected"), before any other status for that connection. Every status change also passes a WebSocketStatusInfo: attempt, hasConnected and, on "reconnecting", nextRetryDelayMs. "reconnecting" with hasConnected: false means the first connect is still failing. With a finite maxReconnectAttempts, running out reports one "disconnected" whose attempt equals the cap, and a later connect() starts a fresh retry budget. Behavior changes: getStatus() returns "reconnecting" while a retry is opening, and an error thrown or rejected by onStatusChange is logged instead of propagated. See WebSockets.
Changed
Fee tiers count volume floors
sdk.fees.getMyFeeTier (GET /api/v1/fees/tier; gRPC FeesService.GetMyFeeTier) resolves currentTierLevel and volumeToNextTier from the higher of the wallet’s weighted 14-day volume and any volume floor Monaco has set on it. weightedVolume14d, spotVolume14d and perpVolume14d still report recorded volume, so a wallet with a floor can show a tier above what weightedVolume14d alone reaches. No fields changed. See Trading.
Margin order admission prices the real cost
A crossing order is charged its initial margin at the mark, the loss each fill takes against the mark, and its taker fee. A resting order is charged its initial margin at its limit, its maker fee, and the loss it would take against the live mark. Only the part of an order that opens or flips a position is charged, so a close is never refused for its loss. The parent margin account’s automatic draw funds exactly what a risk bucket’s orders need beyond its own equity, and every release returns the rest. An adjust-margin transfer prices the amount at the current mark, so the position’s initial margin requirement rises by exactly the amount. No request or response shape changed. See Margin.Collateral bounds round down
withdrawableCollateral and availableOrderCollateral on margin-account and risk-bucket rows, and newWithdrawableCollateral on transfer responses, round down to the collateral asset’s decimals. A transfer of exactly the printed withdrawableCollateral is accepted; availableOrderCollateral is the order-admission bound, not a transfer bound. Risk-bucket figures (isolatedMargin, equity, freeCollateral and the cross liquidationPrice) no longer count the parent’s automatic draw for a resting order against the position.
Fixed
- Account-to-account transfers —
transferMarginCollateralbetween two of your own margin accounts no longer fails with400“token is not a valid 20-byte address”; theassetfield is accepted and ignored on that route. - TP/SL on a just-opened position —
sdk.positions.attachPositionTpSl(POST /api/v1/positions/{positionId}/tp-sl) no longer answers404while the new position is still being persisted. - Adding margin right after open —
transferCollateralToRiskBucket(Adjust Margin) andsdk.copyTrading.addFollowPositionMargincredit a position that has just opened instead of treating the add as a plain risk-bucket allocation. - Margin TWAP on a new risk bucket —
sdk.trading.createTwapOrderon an isolated risk bucket that does not exist yet, or is closed, moves no collateral up front; each slice draws what it needs as it reserves.
Upgrade
page argument and loop on nextPageToken. Replace setServerKey with a clientId on each application listing call. Move TWAP and TP/SL reads off getPaginatedOrders, read summary.byLevel for referral earnings, and add PRICE_BAND and SLIPPAGE_TOLERANCE to any exhaustive terminalReason handling.
