@0xmonaco/core 1.0.46 → 1.0.48

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.
@@ -668,6 +668,10 @@ export class TradingAPIImpl extends BaseAPI {
668
668
  strategyKey: options.strategyKey,
669
669
  useMasterBalance: options.useMasterBalance,
670
670
  clientTwapId: options.clientTwapId,
671
+ executionStyle: options.executionStyle,
672
+ triggerPrice: options.triggerPrice,
673
+ triggerDirection: options.triggerDirection,
674
+ conditionalExpiry: options.conditionalExpiry,
671
675
  };
672
676
  return await this.makeAuthenticatedRequest(perpRoutes.orders.createTwap(), {
673
677
  method: "POST",
@@ -1,6 +1,5 @@
1
- import type { AccountEvent, ConditionalOrderEvent, InstrumentEvent, Interval, LiquidationEvent, MarketStatsAllEvent, MarketStatsEvent, MonacoWebSocketConfig, OHLCVEvent, OHLCVPriceType, OrderbookEvent, OrderbookQuotationMode, OrderEvent, PositionEvent, SessionCredentials, TradeEvent, TradingMode, TwapOrderEvent, UserBalanceEvent, UserMovementEvent, WebSocketStatus } from "@0xmonaco/types";
2
- export type { ResyncHandler, StatusHandler, WebSocketResyncInfo } from "@0xmonaco/types";
3
- export type MessageHandler<T> = (data: T) => void;
1
+ import type { MonacoWebSocketConfig, SessionCredentials } from "@0xmonaco/types";
2
+ export type { MessageHandler, MonacoWebSocket, ResyncHandler, StatusHandler, WebSocketResyncInfo } from "@0xmonaco/types";
4
3
  /**
5
4
  * Options for {@link createMonacoWebSocket}. The reconnect/resync knobs and
6
5
  * callbacks are shared with the main SDK config via {@link MonacoWebSocketConfig}
@@ -11,45 +10,3 @@ export interface MonacoWebSocketOptions extends MonacoWebSocketConfig {
11
10
  /** Session keypair for authenticated channels (orders, balances, etc.) */
12
11
  session?: SessionCredentials;
13
12
  }
14
- export interface MonacoWebSocket {
15
- /** Connect to the WebSocket server */
16
- connect: () => Promise<void>;
17
- /** Disconnect from the WebSocket server */
18
- disconnect: () => void;
19
- /** Check if currently connected */
20
- isConnected: () => boolean;
21
- /** Get current websocket connection status */
22
- getStatus: () => WebSocketStatus;
23
- /** Set (or clear) the session keypair used to authenticate the connection */
24
- setSessionKeypair: (credentials: SessionCredentials | undefined) => void;
25
- /** Subscribe to order events (requires authentication) */
26
- orders: (tradingPairId: string, tradingMode: TradingMode, handler: MessageHandler<OrderEvent>) => () => void;
27
- /** Subscribe to orderbook events (public) */
28
- orderbook: (tradingPairId: string, tradingMode: TradingMode, magnitude: number, quotationMode: OrderbookQuotationMode, handler: MessageHandler<OrderbookEvent>) => () => void;
29
- /** Subscribe to OHLCV candlestick events (public) */
30
- ohlcv: (tradingPairId: string, tradingMode: TradingMode, interval: Interval, handler: MessageHandler<OHLCVEvent>, priceType?: OHLCVPriceType) => () => void;
31
- /** Subscribe to trade events (public) */
32
- trades: (tradingPairId: string, handler: MessageHandler<TradeEvent>) => () => void;
33
- /** Subscribe to user movement events (requires authentication) */
34
- movements: (handler: MessageHandler<UserMovementEvent>) => () => void;
35
- /** Subscribe to user order events for all trading pairs (requires authentication) */
36
- userOrders: (handler: MessageHandler<OrderEvent>) => () => void;
37
- /** Subscribe to user balance events (requires authentication) */
38
- balances: (handler: MessageHandler<UserBalanceEvent>) => () => void;
39
- /** Subscribe to conditional TP/SL lifecycle events (requires authentication) */
40
- conditionalOrders: (handler: MessageHandler<ConditionalOrderEvent>, tradingPairId?: string) => () => void;
41
- /** Subscribe to TWAP parent-order lifecycle events (requires authentication) */
42
- twapOrders: (handler: MessageHandler<TwapOrderEvent>, tradingPairId?: string) => () => void;
43
- /** Subscribe to position state updates (requires authentication); omit `tradingPairId` for all pairs */
44
- positions: (handler: MessageHandler<PositionEvent>, tradingPairId?: string) => () => void;
45
- /** Subscribe to liquidation alerts (requires authentication); omit `tradingPairId` for all pairs */
46
- liquidations: (handler: MessageHandler<LiquidationEvent>, tradingPairId?: string) => () => void;
47
- /** Subscribe to account-level margin health updates for the authenticated user (requires authentication) */
48
- account: (handler: MessageHandler<AccountEvent>) => () => void;
49
- /** Subscribe to market lifecycle + config changes (public); omit `tradingPairId` for all markets */
50
- instruments: (handler: MessageHandler<InstrumentEvent>, tradingPairId?: string) => () => void;
51
- /** Subscribe to per-market statistics for one trading pair (public) */
52
- marketStats: (tradingPairId: string, handler: MessageHandler<MarketStatsEvent>) => () => void;
53
- /** Subscribe to the all-markets statistics summary (public) */
54
- marketStatsAll: (handler: MessageHandler<MarketStatsAllEvent>) => () => void;
55
- }
@@ -7,11 +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 CONDITIONAL_ORDER_REASONS = ["created", "activated", "cancelled", "parent_cancelled", "triggered", "failed", "oco_cancelled"];
11
- const CONDITIONAL_ORDER_CONDITION_TYPES = ["STOP_LOSS", "TAKE_PROFIT"];
12
- const CONDITIONAL_ORDER_TRIGGER_SOURCES = ["MARK_PRICE"];
13
- const CONDITIONAL_ORDER_STATES = ["PENDING_PARENT", "ACTIVE", "TRIGGERING", "TRIGGERED", "CANCELLED", "EXPIRED", "FAILED"];
14
- const CONDITIONAL_ORDER_ASSOCIATION_TYPES = ["POSITION", "PARENT_ORDER"];
15
10
  const TWAP_ORDER_STATES = ["PENDING", "ACTIVE", "COMPLETED", "CANCELLED"];
16
11
  const INSTRUMENT_CHANGE_KINDS = ["listing", "delisting", "halt", "unhalt", "config_change"];
17
12
  function isRecord(value) {
@@ -40,6 +35,19 @@ function readBoolean(source, field) {
40
35
  }
41
36
  return value;
42
37
  }
38
+ /**
39
+ * Read a wire sequence counter: a non-negative safe integer. The server
40
+ * serializes Rust `u64` sequence fields as plain JSON numbers, so a negative,
41
+ * fractional, or unsafe (> 2^53 - 1) value is a malformed frame that would
42
+ * corrupt ordering/dedup logic — reject it rather than pass it through.
43
+ */
44
+ function readSequence(source, field) {
45
+ const value = source[field];
46
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
47
+ throw new Error(`Invalid WebSocket event: ${field} must be a non-negative safe integer`);
48
+ }
49
+ return value;
50
+ }
43
51
  function readOptionalNumber(source, field) {
44
52
  const value = source[field];
45
53
  if (value === undefined || value === null)
@@ -63,14 +71,20 @@ function readEnum(source, field, allowed) {
63
71
  }
64
72
  return value;
65
73
  }
66
- function readOptionalEnum(source, field, allowed) {
67
- const value = source[field];
68
- if (value === undefined || value === null)
69
- return undefined;
70
- if (typeof value !== "string" || !allowed.includes(value)) {
71
- throw new Error(`Invalid WebSocket event: ${field} must be one of ${allowed.join(", ")}`);
72
- }
73
- return value;
74
+ /**
75
+ * Read an optional wire enum leniently.
76
+ *
77
+ * Absent, `null`, and a present-but-empty string all mean "not set": server
78
+ * side these fields are `Option<..>` over closed enums, so `""` is a malformed
79
+ * value rather than a variant this SDK version predates, and letting it through
80
+ * would put a string the union does not contain onto the typed surface. It is
81
+ * mapped to absent rather than thrown on, because throwing would drop the whole
82
+ * frame over one optional field. A non-empty value is passed through untouched,
83
+ * whether or not the SDK recognizes it.
84
+ */
85
+ function readOptionalEnumValue(source, field) {
86
+ const value = readOptionalString(source, field);
87
+ return value === undefined || value.length === 0 ? undefined : value;
74
88
  }
75
89
  function parseConditionalOrderEvent(rawData) {
76
90
  if (!isRecord(rawData)) {
@@ -81,6 +95,13 @@ function parseConditionalOrderEvent(rawData) {
81
95
  }
82
96
  const data = keysToCamelCase(rawData.data);
83
97
  return {
98
+ // event_type is the event-class discriminator, validated strictly so a
99
+ // mis-routed frame is rejected rather than parsed as a conditional order.
100
+ // The value fields below stay lenient — read as strings and cast, never
101
+ // checked against an allow-list — so an unexpected *value* never drops the
102
+ // whole update (0XM-2627). The parse throw is swallowed by the
103
+ // message-handler catch, so a strict enum here means a silently lost frame
104
+ // with nothing surfaced to the consumer.
84
105
  eventType: readEnum(rawData, "event_type", ["conditional_order_update"]),
85
106
  userId: readString(rawData, "user_id"),
86
107
  data: {
@@ -89,27 +110,29 @@ function parseConditionalOrderEvent(rawData) {
89
110
  marginAccountId: readString(data, "marginAccountId"),
90
111
  positionId: readOptionalString(data, "positionId"),
91
112
  parentOrderId: readOptionalString(data, "parentOrderId"),
92
- associationType: readOptionalEnum(data, "associationType", CONDITIONAL_ORDER_ASSOCIATION_TYPES),
113
+ associationType: readOptionalEnumValue(data, "associationType"),
93
114
  linkedGroupId: readOptionalString(data, "linkedGroupId"),
94
- conditionType: readEnum(data, "conditionType", CONDITIONAL_ORDER_CONDITION_TYPES),
95
- triggerSource: readEnum(data, "triggerSource", CONDITIONAL_ORDER_TRIGGER_SOURCES),
115
+ conditionType: readString(data, "conditionType"),
116
+ triggerSource: readString(data, "triggerSource"),
96
117
  triggerPrice: readString(data, "triggerPrice"),
97
- side: readEnum(data, "side", ["BUY", "SELL"]),
98
- positionSide: readEnum(data, "positionSide", ["LONG", "SHORT", "NONE"]),
99
- orderType: readEnum(data, "orderType", ["LIMIT", "MARKET"]),
118
+ side: readString(data, "side"),
119
+ positionSide: readString(data, "positionSide"),
120
+ orderType: readString(data, "orderType"),
100
121
  limitPrice: readOptionalString(data, "limitPrice"),
101
122
  quantity: readOptionalString(data, "quantity"),
102
123
  slippageToleranceBps: readOptionalNumber(data, "slippageToleranceBps"),
103
124
  reduceOnly: readBoolean(data, "reduceOnly"),
104
- timeInForce: readOptionalEnum(data, "timeInForce", ["GTC", "IOC"]),
105
- state: readEnum(data, "state", CONDITIONAL_ORDER_STATES),
125
+ timeInForce: readOptionalEnumValue(data, "timeInForce"),
126
+ state: readString(data, "state"),
106
127
  triggeredOrderId: readOptionalString(data, "triggeredOrderId"),
107
128
  triggeredAt: readOptionalString(data, "triggeredAt"),
108
129
  activatedAt: readOptionalString(data, "activatedAt"),
109
130
  cancelledAt: readOptionalString(data, "cancelledAt"),
110
131
  expiresAt: readOptionalString(data, "expiresAt"),
111
132
  failureReason: readOptionalString(data, "failureReason"),
112
- reason: readEnum(data, "reason", CONDITIONAL_ORDER_REASONS),
133
+ // Free-form on the wire (the server field is a plain string), so it is
134
+ // passed through untouched rather than matched against a value list.
135
+ reason: readString(data, "reason"),
113
136
  updatedAt: readString(data, "updatedAt"),
114
137
  },
115
138
  };
@@ -194,6 +217,10 @@ function parseAccountEvent(rawData) {
194
217
  userId: readString(rawData, "user_id"),
195
218
  data: {
196
219
  marginAccountId: readString(data, "marginAccountId"),
220
+ // Monotonic per-account publish counter (0XM-2498): a plain JSON number on
221
+ // the wire, used to order/dedupe frames across reconnect races. Must be a
222
+ // non-negative safe integer — anything else is a malformed frame.
223
+ sequence: readSequence(data, "sequence"),
197
224
  equity: readString(data, "equity"),
198
225
  collateral: readString(data, "collateral"),
199
226
  freeCollateral: readString(data, "freeCollateral"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmonaco/core",
3
- "version": "1.0.46",
3
+ "version": "1.0.48",
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.46",
23
- "@0xmonaco/types": "1.0.46",
22
+ "@0xmonaco/contracts": "1.0.48",
23
+ "@0xmonaco/types": "1.0.48",
24
24
  "@noble/curves": "^1.9.1",
25
25
  "@noble/hashes": "^1.8.0",
26
26
  "http-status-codes": "^2.3.0"