@0xmonaco/core 1.0.50 → 1.0.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,8 +7,6 @@ const CONNECTION_TIMEOUT = 10000;
7
7
  const HEARTBEAT_INTERVAL = 15000;
8
8
  const MAX_RECONNECT_DELAY = 30000;
9
9
  const RECONNECT_BASE_DELAY = 1000;
10
- const TWAP_ORDER_STATES = ["PENDING", "ACTIVE", "COMPLETED", "CANCELLED"];
11
- const INSTRUMENT_CHANGE_KINDS = ["listing", "delisting", "halt", "unhalt", "config_change"];
12
10
  function isRecord(value) {
13
11
  return typeof value === "object" && value !== null && !Array.isArray(value);
14
12
  }
@@ -134,6 +132,10 @@ function parseConditionalOrderEvent(rawData) {
134
132
  // passed through untouched rather than matched against a value list.
135
133
  reason: readString(data, "reason"),
136
134
  updatedAt: readString(data, "updatedAt"),
135
+ // Populated on the subscribe-time snapshot frame and absent on live
136
+ // updates (0XM-2440). Reading it optionally is what keeps the
137
+ // snapshot's value from being silently dropped before the subscriber.
138
+ version: readOptionalNumber(data, "version"),
137
139
  },
138
140
  };
139
141
  }
@@ -310,7 +312,14 @@ function parseInstrumentEventData(raw) {
310
312
  }
311
313
  const data = keysToCamelCase(raw);
312
314
  return {
313
- change: readEnum(data, "change", INSTRUMENT_CHANGE_KINDS),
315
+ // Read as a plain non-empty string, not matched against a value list: an
316
+ // unrecognized change kind must not throw, because the throw is swallowed
317
+ // by the message-handler catch and takes the whole frame with it — and
318
+ // every instrument frame carries the market's FULL current config, so a
319
+ // dropped frame can hide a market going inactive behind an unfamiliar
320
+ // label. The declared `InstrumentChangeKind` union documents the values
321
+ // the server sends today, not a runtime guarantee.
322
+ change: readString(data, "change"),
314
323
  tradingPairId: readString(data, "tradingPairId"),
315
324
  symbol: readString(data, "symbol"),
316
325
  isActive: readBoolean(data, "isActive"),
@@ -373,9 +382,16 @@ function parseTwapOrderEvent(rawData) {
373
382
  if (typeof twapOrderId !== "string" || twapOrderId.length === 0) {
374
383
  throw new Error("Invalid TWAP order event: twapOrderId must be a non-empty string");
375
384
  }
385
+ // `state` is required, so an absent/non-string/empty one is a malformed
386
+ // frame — but an unrecognized VALUE is passed through untouched. Matching it
387
+ // against a value list would throw, the message-handler catch would swallow
388
+ // the throw, and the whole update would be lost: a TWAP order whose state
389
+ // this SDK version predates would stop reporting its fills entirely
390
+ // (0XM-2627's failure mode). The declared `TwapOrderState` union documents
391
+ // the values the server sends today, not a runtime guarantee.
376
392
  const state = data.state;
377
- if (typeof state !== "string" || !TWAP_ORDER_STATES.includes(state)) {
378
- throw new Error(`Invalid TWAP order event: state must be one of ${TWAP_ORDER_STATES.join(", ")}`);
393
+ if (typeof state !== "string" || state.length === 0) {
394
+ throw new Error("Invalid TWAP order event: state must be a non-empty string");
379
395
  }
380
396
  return {
381
397
  eventType: "twap_order_update",
@@ -553,6 +569,21 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
553
569
  return frameChannel === subscriptionChannel || frameChannel.startsWith(`${subscriptionChannel}:`);
554
570
  };
555
571
  const routeMessage = (msg) => {
572
+ if (msg.type === "Error") {
573
+ // Connection-scoped, not channel-scoped: an Error frame matches no
574
+ // subscription, so without this it reached no consumer at all and a
575
+ // failed subscribe / invalidated session / unreadable subscribe-time
576
+ // snapshot was silent. `code` is passed through whatever its value —
577
+ // a code added server-side after this SDK shipped must still reach the
578
+ // app rather than be dropped for being unrecognized (0XM-2627's
579
+ // leniency contract) — but a frame missing either field is malformed,
580
+ // and synthesizing the missing half would send the app down a branch on
581
+ // data the server never sent.
582
+ if (typeof msg.code === "string" && typeof msg.message === "string") {
583
+ options.onError?.({ code: msg.code, message: msg.message });
584
+ }
585
+ return;
586
+ }
556
587
  if (!msg.channel)
557
588
  return;
558
589
  if (msg.type === "Snapshot") {
@@ -816,7 +847,15 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
816
847
  // Concrete price series of this candle (`trade`/`mark`). Present on
817
848
  // every current frame; a wildcard (`*`) subscriber reads it to tell the
818
849
  // two streams apart. Omitted when a legacy frame carries no `price_type`.
819
- const priceType = data.price_type === "trade" || data.price_type === "mark" ? data.price_type : undefined;
850
+ //
851
+ // Passed through rather than matched against the two known values: a
852
+ // wildcard subscription accepts any suffix, so a series added
853
+ // server-side after this version reaches this parser, and reporting it
854
+ // as `undefined` would leave a wildcard subscriber unable to do the one
855
+ // thing the field exists for. Only a missing or non-string value is
856
+ // treated as absent.
857
+ const rawPriceType = data.price_type;
858
+ const priceType = typeof rawPriceType === "string" && rawPriceType !== "" ? rawPriceType : undefined;
820
859
  const event = {
821
860
  // `trading_pair_id` is the pair UUID; `symbol` is the display string.
822
861
  // `tradingPairId` must carry the UUID (the symbol lives in `candlestick.s`).
@@ -887,12 +926,17 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
887
926
  token: (movementData.token ?? movementData.token_address),
888
927
  tokenAddress: (movementData.token ?? movementData.token_address),
889
928
  assetId: movementData.asset_id,
929
+ symbol: movementData.symbol,
890
930
  amount: String(movementData.amount),
891
931
  amountRaw: movementData.amount_raw,
892
932
  balanceBefore: movementData.balance_before != null ? String(movementData.balance_before) : undefined,
933
+ balanceBeforeRaw: movementData.balance_before_raw,
893
934
  balanceAfter: movementData.balance_after != null ? String(movementData.balance_after) : undefined,
935
+ balanceAfterRaw: movementData.balance_after_raw,
894
936
  lockedBefore: movementData.locked_before != null ? String(movementData.locked_before) : undefined,
937
+ lockedBeforeRaw: movementData.locked_before_raw,
895
938
  lockedAfter: movementData.locked_after != null ? String(movementData.locked_after) : undefined,
939
+ lockedAfterRaw: movementData.locked_after_raw,
896
940
  referenceId: movementData.reference_id,
897
941
  referenceType: movementData.reference_type,
898
942
  description: movementData.description,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmonaco/core",
3
- "version": "1.0.50",
3
+ "version": "1.0.51",
4
4
  "type": "module",
5
5
  "homepage": "https://docs.0xmonaco.com/sdk/typescript",
6
6
  "main": "./dist/index.js",
@@ -19,8 +19,8 @@
19
19
  "viem": "^2.45.2"
20
20
  },
21
21
  "dependencies": {
22
- "@0xmonaco/contracts": "1.0.50",
23
- "@0xmonaco/types": "1.0.50",
22
+ "@0xmonaco/contracts": "1.0.51",
23
+ "@0xmonaco/types": "1.0.51",
24
24
  "@noble/curves": "^1.9.1",
25
25
  "@noble/hashes": "^1.8.0",
26
26
  "http-status-codes": "^2.3.0"