Monaco Protocol SDK v1.0.62
This release covers the consumer-facing changes since v1.0.61. Theaccount WebSocket channel now delivers a subscribe-time snapshot — one row per margin account you own in the authenticated application, flat accounts included — so an immediate baseline no longer needs a margin-accounts REST call; to describe those snapshot rows, AccountEventData.sequence is now optional, which is source-breaking for strict TypeScript. PitPass gains two self-scoped referral reads, getMyReferralPosition and getMyReferralDownline, with matching React hooks and gRPC RPCs, and the verify response now reports isNewUser and referralApplied. The market_stats channels pick up the same snapshot-on-subscribe treatment, and the server-key application order and movement listings now bound total / totalPages at the depth they can page to. If you consume the account channel under strict TypeScript, or read total on the application listings as a lifetime count, read the two upgrade notes before you bump.
Breaking
The account channel snapshot makes AccountEventData.sequence optional
sdk.ws.account(handler, onSnapshot) (authenticated session; raw channel account) now delivers a subscribe-time snapshot ahead of the live diffs: one AccountEventData row per margin account you own in the application you authenticated under, delivered as a single array through the onSnapshot callback. No SDK migration is needed — the onSnapshot parameter already existed on this subscription — but the baseline reaches only subscriptions that pass an onSnapshot handler, so add one if you don’t already. Owning no margin account then delivers an empty array rather than a skipped call.
FLAT accounts are included. The live producer only emits for an account holding an open position or a resting order reserve, so a flat account was silent on the stream until it re-activated; the snapshot carries it with its current persisted health — a zero maintenance requirement and a zero margin ratio. A flat account that is also otherwise empty (no open risk bucket) reports its deposited collateral as both equity and freeCollateral; a flat account still holding an open risk bucket keeps that risk bucket’s allocation and unsettled realized PnL, so its equity need not equal its collateral.
Because a snapshot row is the same payload as a live frame with sequence omitted, the parser reads the field optionally and AccountEventData.sequence is now number | undefined. This is SOURCE-BREAKING for strict TypeScript consumers: event.data.sequence.toFixed(), or passing it where a number is required, no longer compiles. Guard it (event.data.sequence ?? fallback, or narrow with a check) and treat absent as unknown, never zero, and never as a replay or ordering cursor — the counter is the matching engine producer’s process-local per-account map, so a zero from any other emitter is indistinguishable from a restarted producer’s first frame, and it resets on a producer restart. Runtime behaviour is unchanged: live frames still always carry a sequence, and a malformed value on a live frame is still rejected.
Merge the snapshot per marginAccountId rather than replacing your whole channel map, and let any live frame supersede a snapshot row for the same account whatever the timestamps say: a snapshot row’s updatedAt is when its state was persisted (a database write), while a live frame’s is when the engine sampled it, so the two clocks are not comparable under persistence lag. Rank updatedAt only between live frames — newest wins, with sequence breaking ties inside one producer run. Live delivery is also per user rather than per application, so a wallet trading through more than one application still receives live frames for its other accounts; each is full current health and baselines itself.
See Subscribe-time snapshots and the raw account channel reference.
Added
PitPass self-scoped referral reads
sdk.pitpass.getMyReferralPosition() (GET /api/v1/pitpass/referrals/me) and sdk.pitpass.getMyReferralDownline() (GET /api/v1/pitpass/referrals/me/downline) — with the gRPC equivalents TraderCodeService.GetMyReferralPosition and TraderCodeService.GetMyReferralDownline — return the caller’s own referral chain. Both need an authenticated session and are self-scoped: identity is derived from the session, there is no id path parameter, so a request cannot read another user’s chain. Every other user is exposed only as a public wallet address plus a custom-handle-or-wallet display, never an internal id.
getMyReferralPosition returns GetMyReferralPositionResponse: your direct (L1) referrer and referrerDisplay (both empty when you were not referred — only the direct referrer is exposed), the all-time totalEarned across every level and token, and rewards, your most recent ReferralRewardEntry rows newest-first (up to the 100 newest — totalEarned stays the complete aggregate — each carrying the reward’s source, level 1/2/3, amount, token, rateApplied and createdAt).
getMyReferralDownline returns GetMyReferralDownlineResponse: earningsBySource, one ReferralEarningsSource row per (source trader, level, token) you earn from across L1/L2/L3; directReferees, your L1 roster as ReferralDirectReferee rows including referees who have not traded yet (their totalEarned reads "0"), showing up to the 500 most recent effective referees (after per-wallet dedupe and the winning-edge filter), drawn from a bounded scan of recent relationships — so a referrer with a very large history may get an incomplete roster — while the earningsBySource and summary earnings views stay unbounded and count every level, so a caller past that roster cap still reads their full earnings; and summary, a ReferralEarningsSummary carrying totalEarned and the per-level level1Earned / level2Earned / level3Earned — always populated, so a brand-new user reads a zeroed summary rather than an absent field. All amounts are RAW atomic units of the token.
@0xmonaco/react wraps both as the useMyReferralPosition and useMyReferralDownline hooks.
See PitPass and the React PitPass hooks.
The verify response reports isNewUser and referralApplied
AuthState — returned by sdk.auth.authenticate and sdk.auth.verifySignature (POST /api/v1/auth/verify) — now carries two optional flags read from the verify response. isNewUser is true when this verify created the user (the wallet’s first sign-in to this application); referralApplied is true when a PitPass referral relationship was recorded on this verify (a valid TraderCode was applied). Gate first-time UX on isNewUser, and confirm a referral landed with referralApplied. Both are optional, so read them behind a presence check.
See Authentication.
Changed
The market_stats channels deliver a subscribe-time snapshot
sdk.ws.marketStats(pairId, handler, onSnapshot) and sdk.ws.marketStatsAll(handler, onSnapshot) (public, no auth) now receive a subscribe-time snapshot ahead of the live frames. The per-market form delivers a one-element MarketStatsData[] carrying that pair’s latest complete stats frame; the all-markets form delivers a MarketStatsSummary[] with one summary per active market with retained data, ordered by trading-pair id. Each per-market row is the same shape as the live per-market payload, and each all-markets summary is the same shape as an element of the live all-markets frame’s markets list — so apply it as a terminal-state baseline and let the live frames move it forward. No SDK migration is needed — the onSnapshot parameters already existed — but the baseline reaches only subscriptions that pass an onSnapshot handler, so add one if you don’t already.
A per-market subscription whose market has not yet produced a frame — or on a transient matching-engine read failure — receives a SNAPSHOT_UNAVAILABLE signal rather than a fabricated one; the subscription stays active and a later published live frame seeds it once the market produces one (a market that never publishes, e.g. a perp with no open-interest frame, stays unseeded). The all-markets channel returns SNAPSHOT_UNAVAILABLE the same way for a brief window during initial startup or after a server restart (until the producer commits its first full generation), and on any transient snapshot read failure against the matching engine — treat it as a resync signal and take the next periodic push as the baseline. Where a deployment does not run the market-stats snapshot service, neither channel sends a snapshot at all and the first live push seeds the baseline, so do not block on onSnapshot firing.
See Subscribe-time snapshots.
Application listings bound total and totalPages to their pagination reach
client.applications.listApplicationOrders() and client.applications.listApplicationMovements() (GET /api/v1/applications/orders and /api/v1/applications/movements, server-key auth; gRPC ApplicationsService.ListAppOrders / ListAppMovements) now bound their total and totalPages by the listing’s pagination reach. Both stay exact up to 10,000 pages of the requested pageSize (pageSize × 10,000 matching rows) and saturate there, so an application with more matching orders or movements than the listing can page through reads the cap — totalPages becomes 10000, the last page it can reach — instead of its lifetime total. Applications under that reach see the same numbers as before.
The bound lets the server stop counting at the cap rather than walking the whole application’s history on every page render. Loops that stop at page >= totalPages keep terminating; only code that read total as the application’s lifetime count needs to treat exactly pageSize × 10,000 as “at least this many”. On the same two endpoints, a page above 10,000 is now rejected as invalid input — HTTP 400 (error BAD_REQUEST) on REST, INVALID_ARGUMENT on gRPC — instead of fetching rows past the reported last page.
See Application Backend Endpoints.
Upgrade
account channel under strict TypeScript, AccountEventData.sequence is now number | undefined: guard every read (event.data.sequence ?? fallback) and treat absent as unknown rather than zero — snapshot rows omit it, live frames always carry it. And if you read total on the application listings as a lifetime count, it is now capped at pageSize × 10,000; treat exactly that value as “at least this many”, and expect a page above 10,000 to be rejected.
The rest is additive. The account and market_stats subscribe-time snapshots arrive through the optional onSnapshot callback, so a subscription that already passes one starts receiving a baseline with no code change — add the callback if you don’t already; merge the account snapshot per marginAccountId and let live frames supersede it. The PitPass referral reads and the new isNewUser / referralApplied flags are additive surfaces — read the flags behind a presence check.
