@0xmonaco/core 0.6.2 → 0.7.0

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.
Files changed (92) hide show
  1. package/dist/api/auth/api.d.ts +9 -8
  2. package/dist/api/auth/api.js +9 -11
  3. package/dist/api/index.d.ts +3 -0
  4. package/dist/api/index.js +3 -0
  5. package/dist/api/margin-accounts/api.d.ts +12 -0
  6. package/dist/api/margin-accounts/api.js +69 -0
  7. package/dist/api/margin-accounts/index.d.ts +1 -0
  8. package/dist/api/margin-accounts/index.js +1 -0
  9. package/dist/api/market/api.d.ts +8 -1
  10. package/dist/api/market/api.js +31 -3
  11. package/dist/api/orderbook/api.js +1 -1
  12. package/dist/api/perp/index.d.ts +1 -0
  13. package/dist/api/perp/index.js +1 -0
  14. package/dist/api/perp/routes.d.ts +133 -0
  15. package/dist/api/perp/routes.js +85 -0
  16. package/dist/api/positions/api.d.ts +12 -0
  17. package/dist/api/positions/api.js +88 -0
  18. package/dist/api/positions/index.d.ts +1 -0
  19. package/dist/api/positions/index.js +1 -0
  20. package/dist/api/trades/api.d.ts +1 -1
  21. package/dist/api/trades/api.js +2 -2
  22. package/dist/api/trading/api.d.ts +14 -3
  23. package/dist/api/trading/api.js +76 -15
  24. package/dist/api/websocket/types.d.ts +3 -1
  25. package/dist/api/websocket/websocket.js +153 -17
  26. package/dist/sdk.d.ts +7 -5
  27. package/dist/sdk.js +17 -9
  28. package/package.json +1 -1
  29. package/dist/api/applications/api.d.ts.map +0 -1
  30. package/dist/api/applications/api.js.map +0 -1
  31. package/dist/api/applications/index.d.ts.map +0 -1
  32. package/dist/api/applications/index.js.map +0 -1
  33. package/dist/api/auth/api.d.ts.map +0 -1
  34. package/dist/api/auth/api.js.map +0 -1
  35. package/dist/api/auth/index.d.ts.map +0 -1
  36. package/dist/api/auth/index.js.map +0 -1
  37. package/dist/api/base.d.ts.map +0 -1
  38. package/dist/api/base.js.map +0 -1
  39. package/dist/api/fees/api.d.ts.map +0 -1
  40. package/dist/api/fees/api.js.map +0 -1
  41. package/dist/api/fees/index.d.ts.map +0 -1
  42. package/dist/api/fees/index.js.map +0 -1
  43. package/dist/api/index.d.ts.map +0 -1
  44. package/dist/api/index.js.map +0 -1
  45. package/dist/api/market/api.d.ts.map +0 -1
  46. package/dist/api/market/api.js.map +0 -1
  47. package/dist/api/market/index.d.ts.map +0 -1
  48. package/dist/api/market/index.js.map +0 -1
  49. package/dist/api/orderbook/api.d.ts.map +0 -1
  50. package/dist/api/orderbook/api.js.map +0 -1
  51. package/dist/api/orderbook/index.d.ts.map +0 -1
  52. package/dist/api/orderbook/index.js.map +0 -1
  53. package/dist/api/profile/api.d.ts.map +0 -1
  54. package/dist/api/profile/api.js.map +0 -1
  55. package/dist/api/profile/index.d.ts.map +0 -1
  56. package/dist/api/profile/index.js.map +0 -1
  57. package/dist/api/trades/api.d.ts.map +0 -1
  58. package/dist/api/trades/api.js.map +0 -1
  59. package/dist/api/trades/index.d.ts.map +0 -1
  60. package/dist/api/trades/index.js.map +0 -1
  61. package/dist/api/trading/api.d.ts.map +0 -1
  62. package/dist/api/trading/api.js.map +0 -1
  63. package/dist/api/trading/index.d.ts.map +0 -1
  64. package/dist/api/trading/index.js.map +0 -1
  65. package/dist/api/vault/api.d.ts.map +0 -1
  66. package/dist/api/vault/api.js.map +0 -1
  67. package/dist/api/vault/index.d.ts.map +0 -1
  68. package/dist/api/vault/index.js.map +0 -1
  69. package/dist/api/websocket/index.d.ts.map +0 -1
  70. package/dist/api/websocket/index.js.map +0 -1
  71. package/dist/api/websocket/types.d.ts.map +0 -1
  72. package/dist/api/websocket/types.js.map +0 -1
  73. package/dist/api/websocket/utils.d.ts.map +0 -1
  74. package/dist/api/websocket/utils.js.map +0 -1
  75. package/dist/api/websocket/websocket.d.ts.map +0 -1
  76. package/dist/api/websocket/websocket.js.map +0 -1
  77. package/dist/errors/errors.d.ts.map +0 -1
  78. package/dist/errors/errors.js.map +0 -1
  79. package/dist/errors/index.d.ts.map +0 -1
  80. package/dist/errors/index.js.map +0 -1
  81. package/dist/index.d.ts.map +0 -1
  82. package/dist/index.js.map +0 -1
  83. package/dist/networks/index.d.ts.map +0 -1
  84. package/dist/networks/index.js.map +0 -1
  85. package/dist/networks/networks.d.ts.map +0 -1
  86. package/dist/networks/networks.js.map +0 -1
  87. package/dist/sdk.d.ts.map +0 -1
  88. package/dist/sdk.js.map +0 -1
  89. package/dist/utils/index.d.ts.map +0 -1
  90. package/dist/utils/index.js.map +0 -1
  91. package/dist/utils/magnitude.d.ts.map +0 -1
  92. package/dist/utils/magnitude.js.map +0 -1
@@ -21,7 +21,7 @@
21
21
  * );
22
22
  * ```
23
23
  */
24
- import type { BatchCancelOrdersResponse, BatchCreateOrderParams, BatchCreateOrdersResponse, BatchReplaceOrderParams, BatchReplaceOrdersResponse, CancelOrderResponse, CreateOrderResponse, GetOrderResponse, GetPaginatedOrdersParams, GetPaginatedOrdersResponse, OrderSide, ReplaceOrderResponse, TimeInForce, TradingAPI, TradingMode } from "@0xmonaco/types";
24
+ import type { BatchCancelOrdersResponse, BatchCreateOrderParams, BatchCreateOrdersResponse, BatchReplaceOrderParams, BatchReplaceOrdersResponse, CancelConditionalOrderResponse, CancelOrderResponse, CreateConditionalOrderParams, CreateConditionalOrderResponse, CreateOrderResponse, GetOrderResponse, GetPaginatedOrdersParams, GetPaginatedOrdersResponse, ListConditionalOrdersParams, ListConditionalOrdersResponse, OrderSide, PositionSide, ReplaceOrderResponse, TimeInForce, TradingAPI, TradingMode } from "@0xmonaco/types";
25
25
  import { BaseAPI } from "../base";
26
26
  export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
27
27
  /**
@@ -79,6 +79,10 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
79
79
  useMasterBalance?: boolean;
80
80
  expirationDate?: string;
81
81
  timeInForce?: TimeInForce;
82
+ marginAccountId?: string;
83
+ positionSide?: PositionSide;
84
+ leverage?: string;
85
+ reduceOnly?: boolean;
82
86
  }): Promise<CreateOrderResponse>;
83
87
  /**
84
88
  * Places a market order for immediate execution.
@@ -124,6 +128,10 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
124
128
  placeMarketOrder(tradingPairId: string, side: OrderSide, quantity: string, options?: {
125
129
  tradingMode?: TradingMode;
126
130
  slippageTolerance?: number;
131
+ marginAccountId?: string;
132
+ positionSide?: PositionSide;
133
+ leverage?: string;
134
+ reduceOnly?: boolean;
127
135
  }): Promise<CreateOrderResponse>;
128
136
  /**
129
137
  * Cancels an existing order.
@@ -142,6 +150,9 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
142
150
  * ```
143
151
  */
144
152
  cancelOrder(orderId: string): Promise<CancelOrderResponse>;
153
+ createConditionalOrder(params: CreateConditionalOrderParams): Promise<CreateConditionalOrderResponse>;
154
+ cancelConditionalOrder(conditionalOrderId: string): Promise<CancelConditionalOrderResponse>;
155
+ listConditionalOrders(params?: ListConditionalOrdersParams): Promise<ListConditionalOrdersResponse>;
145
156
  /**
146
157
  * Batch cancels specific orders by their IDs.
147
158
  *
@@ -242,7 +253,7 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
242
253
  *
243
254
  * @param params - Query parameters for filtering orders
244
255
  * @param params.status - Filter by order status (e.g., "SUBMITTED", "FILLED") (optional)
245
- * @param params.trading_pair - Filter by trading pair (e.g., "USDCo/MTK") (optional)
256
+ * @param params.trading_pair_id - Filter by trading pair UUID (optional)
246
257
  * @param params.page - Page number for pagination (defaults to 1, must be > 0)
247
258
  * @param params.page_size - Number of orders per page (defaults to 10, max 100, must be > 0)
248
259
  * @returns Promise resolving to GetPaginatedOrdersResponse with orders and pagination info
@@ -253,7 +264,7 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
253
264
  * // Get submitted orders for a specific trading pair with custom pagination
254
265
  * const orders = await tradingAPI.getPaginatedOrders({
255
266
  * status: "SUBMITTED",
256
- * trading_pair: "USDCo/MTK",
267
+ * trading_pair_id: "456e7890-e12b-12d3-a456-426614174000",
257
268
  * page: 1,
258
269
  * page_size: 20
259
270
  * });
@@ -21,8 +21,9 @@
21
21
  * );
22
22
  * ```
23
23
  */
24
- import { BatchCreateOrdersSchema, BatchReplaceOrdersSchema, CancelOrderSchema, GetPaginatedOrdersSchema, PlaceLimitOrderSchema, PlaceMarketOrderSchema, ReplaceOrderSchema, validate, } from "@0xmonaco/types";
24
+ import { BatchCreateOrdersSchema, BatchReplaceOrdersSchema, CancelConditionalOrderSchema, CancelOrderSchema, CreateConditionalOrderSchema, GetPaginatedOrdersSchema, ListConditionalOrdersSchema, PlaceLimitOrderSchema, PlaceMarketOrderSchema, ReplaceOrderSchema, validate, } from "@0xmonaco/types";
25
25
  import { BaseAPI } from "../base";
26
+ import { perpRoutes } from "../perp";
26
27
  export class TradingAPIImpl extends BaseAPI {
27
28
  /**
28
29
  * Places a limit order on the order book.
@@ -93,8 +94,12 @@ export class TradingAPIImpl extends BaseAPI {
93
94
  use_master_balance: options?.useMasterBalance,
94
95
  expiration_date: options?.expirationDate,
95
96
  time_in_force: options?.timeInForce,
97
+ margin_account_id: options?.marginAccountId,
98
+ position_side: options?.positionSide,
99
+ leverage: options?.leverage,
100
+ reduce_only: options?.reduceOnly,
96
101
  };
97
- return await this.makeAuthenticatedRequest("/api/v1/orders", {
102
+ return await this.makeAuthenticatedRequest(perpRoutes.orders.create(), {
98
103
  method: "POST",
99
104
  body: JSON.stringify(requestBody),
100
105
  });
@@ -156,8 +161,12 @@ export class TradingAPIImpl extends BaseAPI {
156
161
  price: null, // Market orders don't need price
157
162
  quantity,
158
163
  trading_mode: options?.tradingMode || "SPOT",
164
+ margin_account_id: options?.marginAccountId,
165
+ position_side: options?.positionSide,
166
+ leverage: options?.leverage,
167
+ reduce_only: options?.reduceOnly,
159
168
  };
160
- return await this.makeAuthenticatedRequest("/api/v1/orders", {
169
+ return await this.makeAuthenticatedRequest(perpRoutes.orders.create(), {
161
170
  method: "POST",
162
171
  body: JSON.stringify(requestBody),
163
172
  });
@@ -184,11 +193,53 @@ export class TradingAPIImpl extends BaseAPI {
184
193
  const requestBody = {
185
194
  order_id: orderId,
186
195
  };
187
- return await this.makeAuthenticatedRequest("/api/v1/orders/cancel", {
196
+ return await this.makeAuthenticatedRequest(perpRoutes.orders.cancel(), {
188
197
  method: "POST",
189
198
  body: JSON.stringify(requestBody),
190
199
  });
191
200
  }
201
+ async createConditionalOrder(params) {
202
+ validate(CreateConditionalOrderSchema, params);
203
+ const requestBody = {
204
+ trading_pair_id: params.tradingPairId,
205
+ margin_account_id: params.marginAccountId,
206
+ condition_type: params.conditionType,
207
+ trigger_price: params.triggerPrice,
208
+ trigger_source: params.triggerSource ?? "MARK_PRICE",
209
+ side: params.side,
210
+ position_side: params.positionSide,
211
+ order_type: params.orderType,
212
+ limit_price: params.limitPrice,
213
+ quantity: params.quantity,
214
+ reduce_only: params.reduceOnly ?? true,
215
+ time_in_force: params.timeInForce,
216
+ slippage_tolerance_bps: params.slippageToleranceBps,
217
+ expires_at: params.expiresAt,
218
+ };
219
+ return await this.makeAuthenticatedRequest(perpRoutes.orders.createConditional(), {
220
+ method: "POST",
221
+ body: JSON.stringify(requestBody),
222
+ });
223
+ }
224
+ async cancelConditionalOrder(conditionalOrderId) {
225
+ validate(CancelConditionalOrderSchema, { conditionalOrderId });
226
+ return await this.makeAuthenticatedRequest(perpRoutes.orders.cancelConditional(conditionalOrderId), {
227
+ method: "DELETE",
228
+ });
229
+ }
230
+ async listConditionalOrders(params) {
231
+ if (params) {
232
+ validate(ListConditionalOrdersSchema, params);
233
+ }
234
+ const { page = 1, page_size = 20, margin_account_id, trading_pair_id, state } = params || {};
235
+ return await this.makeAuthenticatedRequest(perpRoutes.orders.listConditional({
236
+ page,
237
+ page_size: Math.min(Math.max(page_size, 1), 100),
238
+ margin_account_id,
239
+ trading_pair_id,
240
+ state,
241
+ }), { method: "GET" });
242
+ }
192
243
  /**
193
244
  * Batch cancels specific orders by their IDs.
194
245
  *
@@ -205,7 +256,7 @@ export class TradingAPIImpl extends BaseAPI {
205
256
  if (!orderIds || orderIds.length === 0) {
206
257
  throw new Error("orderIds is required and must not be empty");
207
258
  }
208
- return this.makeAuthenticatedRequest("/api/v1/orders/batch-cancel", {
259
+ return this.makeAuthenticatedRequest(perpRoutes.orders.batchCancel(), {
209
260
  method: "POST",
210
261
  body: JSON.stringify({ order_ids: orderIds }),
211
262
  });
@@ -227,7 +278,7 @@ export class TradingAPIImpl extends BaseAPI {
227
278
  * ```
228
279
  */
229
280
  async batchCancelAll(tradingPairId) {
230
- const endpoint = tradingPairId ? `/api/v1/orders/batch-cancel-all/${tradingPairId}` : "/api/v1/orders/batch-cancel-all";
281
+ const endpoint = tradingPairId ? perpRoutes.orders.batchCancelAllByPair(tradingPairId) : perpRoutes.orders.batchCancelAll();
231
282
  return this.makeAuthenticatedRequest(endpoint, {
232
283
  method: "POST",
233
284
  });
@@ -244,7 +295,7 @@ export class TradingAPIImpl extends BaseAPI {
244
295
  if (newOrder.quantity !== undefined) {
245
296
  requestBody.quantity = newOrder.quantity;
246
297
  }
247
- return await this.makeAuthenticatedRequest(`/api/v1/orders/${orderId}`, {
298
+ return await this.makeAuthenticatedRequest(perpRoutes.orders.replace(orderId), {
248
299
  method: "PUT",
249
300
  body: JSON.stringify(requestBody),
250
301
  });
@@ -293,9 +344,13 @@ export class TradingAPIImpl extends BaseAPI {
293
344
  use_master_balance: order.useMasterBalance,
294
345
  expiration_date: order.expirationDate,
295
346
  time_in_force: order.timeInForce,
347
+ margin_account_id: order.marginAccountId,
348
+ position_side: order.positionSide,
349
+ leverage: order.leverage,
350
+ reduce_only: order.reduceOnly,
296
351
  })),
297
352
  };
298
- return this.makeAuthenticatedRequest("/api/v1/orders/batch-create", {
353
+ return this.makeAuthenticatedRequest(perpRoutes.orders.batchCreate(), {
299
354
  method: "POST",
300
355
  body: JSON.stringify(requestBody),
301
356
  });
@@ -335,7 +390,7 @@ export class TradingAPIImpl extends BaseAPI {
335
390
  use_master_balance: order.useMasterBalance,
336
391
  })),
337
392
  };
338
- return this.makeAuthenticatedRequest("/api/v1/orders/batch-replace", {
393
+ return this.makeAuthenticatedRequest(perpRoutes.orders.batchReplace(), {
339
394
  method: "POST",
340
395
  body: JSON.stringify(requestBody),
341
396
  });
@@ -348,7 +403,7 @@ export class TradingAPIImpl extends BaseAPI {
348
403
  *
349
404
  * @param params - Query parameters for filtering orders
350
405
  * @param params.status - Filter by order status (e.g., "SUBMITTED", "FILLED") (optional)
351
- * @param params.trading_pair - Filter by trading pair (e.g., "USDCo/MTK") (optional)
406
+ * @param params.trading_pair_id - Filter by trading pair UUID (optional)
352
407
  * @param params.page - Page number for pagination (defaults to 1, must be > 0)
353
408
  * @param params.page_size - Number of orders per page (defaults to 10, max 100, must be > 0)
354
409
  * @returns Promise resolving to GetPaginatedOrdersResponse with orders and pagination info
@@ -359,7 +414,7 @@ export class TradingAPIImpl extends BaseAPI {
359
414
  * // Get submitted orders for a specific trading pair with custom pagination
360
415
  * const orders = await tradingAPI.getPaginatedOrders({
361
416
  * status: "SUBMITTED",
362
- * trading_pair: "USDCo/MTK",
417
+ * trading_pair_id: "456e7890-e12b-12d3-a456-426614174000",
363
418
  * page: 1,
364
419
  * page_size: 20
365
420
  * });
@@ -375,7 +430,7 @@ export class TradingAPIImpl extends BaseAPI {
375
430
  validate(GetPaginatedOrdersSchema, params);
376
431
  }
377
432
  // Set pagination defaults with destructuring and validation
378
- const { page = 1, page_size = 10, status, trading_pair } = params || {};
433
+ const { page = 1, page_size = 10, status, trading_pair_id, trading_mode, margin_account_id } = params || {};
379
434
  const validPage = page > 0 ? page : 1;
380
435
  const validPageSize = page_size > 0 ? page_size : 10;
381
436
  const pageSize = Math.min(validPageSize, 100);
@@ -386,8 +441,14 @@ export class TradingAPIImpl extends BaseAPI {
386
441
  if (status) {
387
442
  searchParams.append("status", status);
388
443
  }
389
- if (trading_pair) {
390
- searchParams.append("trading_pair", trading_pair);
444
+ if (trading_pair_id) {
445
+ searchParams.append("trading_pair_id", trading_pair_id);
446
+ }
447
+ if (trading_mode) {
448
+ searchParams.append("trading_mode", trading_mode);
449
+ }
450
+ if (margin_account_id) {
451
+ searchParams.append("margin_account_id", margin_account_id);
391
452
  }
392
453
  const endpoint = `/api/v1/orders?${searchParams.toString()}`;
393
454
  return await this.makeAuthenticatedRequest(endpoint, {
@@ -413,7 +474,7 @@ export class TradingAPIImpl extends BaseAPI {
413
474
  * ```
414
475
  */
415
476
  async getOrder(orderId) {
416
- return await this.makeAuthenticatedRequest(`/api/v1/orders/${orderId}`, {
477
+ return await this.makeAuthenticatedRequest(perpRoutes.orders.get(orderId), {
417
478
  method: "GET",
418
479
  });
419
480
  }
@@ -1,4 +1,4 @@
1
- import type { Interval, OHLCVEvent, OrderbookEvent, OrderbookQuotationMode, OrderEvent, TradeEvent, TradingMode, UserBalanceEvent, UserMovementEvent, WebSocketStatus } from "@0xmonaco/types";
1
+ import type { ConditionalOrderEvent, Interval, OHLCVEvent, OrderbookEvent, OrderbookQuotationMode, OrderEvent, TradeEvent, TradingMode, UserBalanceEvent, UserMovementEvent, WebSocketStatus } from "@0xmonaco/types";
2
2
  export type StatusHandler = (status: WebSocketStatus) => void;
3
3
  export type MessageHandler<T> = (data: T) => void;
4
4
  export interface MonacoWebSocketOptions {
@@ -36,4 +36,6 @@ export interface MonacoWebSocket {
36
36
  userOrders: (handler: MessageHandler<OrderEvent>) => () => void;
37
37
  /** Subscribe to user balance events (requires authentication) */
38
38
  balances: (handler: MessageHandler<UserBalanceEvent>) => () => void;
39
+ /** Subscribe to conditional TP/SL lifecycle events (requires authentication) */
40
+ conditionalOrders: (handler: MessageHandler<ConditionalOrderEvent>, tradingPairId?: string) => () => void;
39
41
  }
@@ -4,6 +4,100 @@ import { keysToCamelCase } from "./utils";
4
4
  const CONNECTION_TIMEOUT = 10000;
5
5
  const HEARTBEAT_INTERVAL = 15000;
6
6
  const MAX_RECONNECT_DELAY = 30000;
7
+ const CONDITIONAL_ORDER_REASONS = ["created", "cancelled", "triggered", "failed", "oco_cancelled"];
8
+ const CONDITIONAL_ORDER_CONDITION_TYPES = ["STOP_LOSS", "TAKE_PROFIT"];
9
+ const CONDITIONAL_ORDER_TRIGGER_SOURCES = ["MARK_PRICE"];
10
+ const CONDITIONAL_ORDER_STATES = ["ACTIVE", "TRIGGERING", "TRIGGERED", "CANCELLED", "EXPIRED", "FAILED"];
11
+ function isRecord(value) {
12
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13
+ }
14
+ function readString(source, field) {
15
+ const value = source[field];
16
+ if (typeof value !== "string" || value.length === 0) {
17
+ throw new Error(`Invalid conditional order event: ${field} must be a non-empty string`);
18
+ }
19
+ return value;
20
+ }
21
+ function readOptionalString(source, field) {
22
+ const value = source[field];
23
+ if (value === undefined || value === null)
24
+ return undefined;
25
+ if (typeof value !== "string") {
26
+ throw new Error(`Invalid conditional order event: ${field} must be a string`);
27
+ }
28
+ return value;
29
+ }
30
+ function readBoolean(source, field) {
31
+ const value = source[field];
32
+ if (typeof value !== "boolean") {
33
+ throw new Error(`Invalid conditional order event: ${field} must be a boolean`);
34
+ }
35
+ return value;
36
+ }
37
+ function readOptionalNumber(source, field) {
38
+ const value = source[field];
39
+ if (value === undefined || value === null)
40
+ return undefined;
41
+ if (typeof value !== "number" || !Number.isFinite(value)) {
42
+ throw new Error(`Invalid conditional order event: ${field} must be a finite number`);
43
+ }
44
+ return value;
45
+ }
46
+ function readEnum(source, field, allowed) {
47
+ const value = readString(source, field);
48
+ if (!allowed.includes(value)) {
49
+ throw new Error(`Invalid conditional order event: ${field} must be one of ${allowed.join(", ")}`);
50
+ }
51
+ return value;
52
+ }
53
+ function readOptionalEnum(source, field, allowed) {
54
+ const value = source[field];
55
+ if (value === undefined || value === null)
56
+ return undefined;
57
+ if (typeof value !== "string" || !allowed.includes(value)) {
58
+ throw new Error(`Invalid conditional order event: ${field} must be one of ${allowed.join(", ")}`);
59
+ }
60
+ return value;
61
+ }
62
+ function parseConditionalOrderEvent(rawData) {
63
+ if (!isRecord(rawData)) {
64
+ throw new Error("Invalid conditional order event: payload must be an object");
65
+ }
66
+ if (!isRecord(rawData.data)) {
67
+ throw new Error("Invalid conditional order event: data must be an object");
68
+ }
69
+ const data = keysToCamelCase(rawData.data);
70
+ return {
71
+ eventType: readEnum(rawData, "event_type", ["conditional_order_update"]),
72
+ userId: readString(rawData, "user_id"),
73
+ data: {
74
+ conditionalOrderId: readString(data, "conditionalOrderId"),
75
+ tradingPairId: readString(data, "tradingPairId"),
76
+ marginAccountId: readString(data, "marginAccountId"),
77
+ positionId: readOptionalString(data, "positionId"),
78
+ linkedGroupId: readOptionalString(data, "linkedGroupId"),
79
+ conditionType: readEnum(data, "conditionType", CONDITIONAL_ORDER_CONDITION_TYPES),
80
+ triggerSource: readEnum(data, "triggerSource", CONDITIONAL_ORDER_TRIGGER_SOURCES),
81
+ triggerPrice: readString(data, "triggerPrice"),
82
+ side: readEnum(data, "side", ["BUY", "SELL"]),
83
+ positionSide: readEnum(data, "positionSide", ["LONG", "SHORT", "NONE"]),
84
+ orderType: readEnum(data, "orderType", ["LIMIT", "MARKET"]),
85
+ limitPrice: readOptionalString(data, "limitPrice"),
86
+ quantity: readOptionalString(data, "quantity"),
87
+ slippageToleranceBps: readOptionalNumber(data, "slippageToleranceBps"),
88
+ reduceOnly: readBoolean(data, "reduceOnly"),
89
+ timeInForce: readOptionalEnum(data, "timeInForce", ["GTC", "IOC"]),
90
+ state: readEnum(data, "state", CONDITIONAL_ORDER_STATES),
91
+ triggeredOrderId: readOptionalString(data, "triggeredOrderId"),
92
+ triggeredAt: readOptionalString(data, "triggeredAt"),
93
+ cancelledAt: readOptionalString(data, "cancelledAt"),
94
+ expiresAt: readOptionalString(data, "expiresAt"),
95
+ failureReason: readOptionalString(data, "failureReason"),
96
+ reason: readEnum(data, "reason", CONDITIONAL_ORDER_REASONS),
97
+ updatedAt: readString(data, "updatedAt"),
98
+ },
99
+ };
100
+ }
7
101
  /**
8
102
  * Create a Monaco WebSocket client
9
103
  */
@@ -62,7 +156,11 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
62
156
  }
63
157
  reconnectAttempts++;
64
158
  const delay = Math.min(1000 * 2 ** reconnectAttempts, MAX_RECONNECT_DELAY);
65
- reconnectTimer = setTimeout(() => connect(), delay);
159
+ reconnectTimer = setTimeout(() => {
160
+ connect().catch((err) => {
161
+ console.warn("WebSocket: Failed to reconnect:", err);
162
+ });
163
+ }, delay);
66
164
  };
67
165
  const send = (data) => {
68
166
  if (ws?.readyState === WebSocket.OPEN) {
@@ -84,7 +182,6 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
84
182
  for (const handler of channelHandlers) {
85
183
  handler(msg.data);
86
184
  }
87
- return;
88
185
  }
89
186
  }
90
187
  };
@@ -96,22 +193,33 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
96
193
  return;
97
194
  }
98
195
  try {
99
- ws = new WebSocket(getUrl());
196
+ const socket = new WebSocket(getUrl());
197
+ ws = socket;
100
198
  const timeout = setTimeout(() => {
101
- if (ws?.readyState === WebSocket.CONNECTING) {
102
- ws.close();
199
+ if (ws !== socket) {
200
+ resolve();
201
+ return;
202
+ }
203
+ if (socket.readyState === WebSocket.CONNECTING) {
204
+ socket.close(1000, "Connection timeout");
103
205
  reject(new Error("WebSocket connection timeout"));
104
206
  }
105
207
  }, CONNECTION_TIMEOUT);
106
- ws.onopen = () => {
208
+ socket.onopen = () => {
107
209
  clearTimeout(timeout);
210
+ if (ws !== socket) {
211
+ resolve();
212
+ return;
213
+ }
108
214
  reconnectAttempts = 0;
109
215
  startHeartbeat();
110
216
  resubscribeAll();
111
217
  options.onStatusChange?.("connected");
112
218
  resolve();
113
219
  };
114
- ws.onmessage = (event) => {
220
+ socket.onmessage = (event) => {
221
+ if (ws !== socket)
222
+ return;
115
223
  try {
116
224
  const msg = JSON.parse(event.data);
117
225
  // Handle pong silently
@@ -123,8 +231,13 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
123
231
  console.warn("WebSocket: Failed to parse message", err);
124
232
  }
125
233
  };
126
- ws.onclose = (event) => {
234
+ socket.onclose = (event) => {
127
235
  clearTimeout(timeout);
236
+ if (ws !== socket) {
237
+ resolve();
238
+ return;
239
+ }
240
+ ws = null;
128
241
  stopHeartbeat();
129
242
  options.onStatusChange?.("disconnected");
130
243
  // Reconnect on abnormal close
@@ -132,7 +245,7 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
132
245
  scheduleReconnect();
133
246
  }
134
247
  };
135
- ws.onerror = () => clearTimeout(timeout);
248
+ socket.onerror = () => clearTimeout(timeout);
136
249
  }
137
250
  catch (err) {
138
251
  options.onStatusChange?.("disconnected");
@@ -207,7 +320,7 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
207
320
  const data = rawData;
208
321
  const orderbookData = data.data;
209
322
  const event = {
210
- tradingPairId: data.pair,
323
+ tradingPairId: data.symbol,
211
324
  tradingMode: data.trading_mode,
212
325
  bids: (orderbookData?.bids || []).map((level) => ({
213
326
  price: level.price,
@@ -251,7 +364,7 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
251
364
  const data = rawData;
252
365
  const ohlcvData = data.data;
253
366
  const event = {
254
- tradingPairId: data.pair,
367
+ tradingPairId: data.symbol,
255
368
  tradingMode: data.trading_mode,
256
369
  interval: data.interval,
257
370
  candlestick: {
@@ -262,7 +375,7 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
262
375
  l: ohlcvData.low || "0",
263
376
  c: ohlcvData.close || "0",
264
377
  v: ohlcvData.volume || "0",
265
- s: data.pair,
378
+ s: data.symbol,
266
379
  i: data.interval,
267
380
  n: ohlcvData.trades_count || 0,
268
381
  },
@@ -282,7 +395,7 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
282
395
  const tradeData = data.data;
283
396
  const event = {
284
397
  eventType: "trade",
285
- tradingPairId: data.pair_id,
398
+ tradingPairId: data.trading_pair_id,
286
399
  tradingMode: data.trading_mode.toUpperCase(),
287
400
  data: {
288
401
  tradeId: tradeData.trade_id,
@@ -393,18 +506,40 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
393
506
  }
394
507
  });
395
508
  };
509
+ const subscribeConditionalOrders = (handler, tradingPairId) => {
510
+ if (!handler) {
511
+ throw new Error("conditionalOrders subscription requires a handler");
512
+ }
513
+ if (tradingPairId !== undefined && tradingPairId.length === 0) {
514
+ throw new Error("conditionalOrders tradingPairId cannot be empty");
515
+ }
516
+ const channel = tradingPairId ? `conditional_orders:${tradingPairId}` : "conditional_orders";
517
+ return subscribe(channel, (rawData) => {
518
+ try {
519
+ handler(parseConditionalOrderEvent(rawData));
520
+ }
521
+ catch (err) {
522
+ console.error("WebSocket: Error processing conditional order event", err);
523
+ }
524
+ });
525
+ };
396
526
  return {
397
527
  connect,
398
528
  disconnect,
399
529
  isConnected: () => ws?.readyState === WebSocket.OPEN,
400
530
  getStatus,
401
531
  setToken: (newToken) => {
402
- token = newToken;
403
- // Reconnect to include token in ws URL
404
- if (ws?.readyState === WebSocket.OPEN || ws?.readyState === WebSocket.CONNECTING) {
405
- ws.close(1000, "Token updated, reconnecting.");
532
+ token = newToken || undefined;
533
+ stopReconnect();
534
+ const currentSocket = ws;
535
+ if (currentSocket?.readyState === WebSocket.OPEN ||
536
+ currentSocket?.readyState === WebSocket.CONNECTING ||
537
+ currentSocket?.readyState === WebSocket.CLOSING) {
538
+ currentSocket.close(1000, newToken ? "Token updated, reconnecting." : "Token cleared.");
406
539
  ws = null;
407
540
  }
541
+ if (!newToken)
542
+ return;
408
543
  connect().catch((err) => {
409
544
  console.warn("WebSocket: Failed to reconnect after token update:", err);
410
545
  });
@@ -416,5 +551,6 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
416
551
  movements: subscribeMovements,
417
552
  userOrders: subscribeUserOrders,
418
553
  balances: subscribeBalances,
554
+ conditionalOrders: subscribeConditionalOrders,
419
555
  };
420
556
  }
package/dist/sdk.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ApplicationsAPI, AuthAPI, AuthState, FeesAPI, MarketAPI, MonacoSDK, Network, ProfileAPI, SDKConfig, TradingAPI, VaultAPI } from "@0xmonaco/types";
1
+ import type { ApplicationsAPI, AuthAPI, AuthState, FeesAPI, MarginAccountsAPI, MarketAPI, MonacoSDK, Network, PositionsAPI, ProfileAPI, SDKConfig, TradingAPI, VaultAPI } from "@0xmonaco/types";
2
2
  import { type PublicClient, type TransactionReceipt, type WalletClient } from "viem";
3
3
  import { type MonacoWebSocket, OrderbookAPIImpl, TradesAPIImpl } from "./api";
4
4
  export declare class MonacoSDKImpl implements MonacoSDK {
@@ -8,6 +8,8 @@ export declare class MonacoSDKImpl implements MonacoSDK {
8
8
  vault: VaultAPI;
9
9
  trading: TradingAPI;
10
10
  market: MarketAPI;
11
+ marginAccounts: MarginAccountsAPI;
12
+ positions: PositionsAPI;
11
13
  profile: ProfileAPI;
12
14
  orderbook: OrderbookAPIImpl;
13
15
  trades: TradesAPIImpl;
@@ -31,8 +33,8 @@ export declare class MonacoSDKImpl implements MonacoSDK {
31
33
  * - `expiresAt`: When the access token expires
32
34
  * - `user`: User information
33
35
  *
34
- * Note: There is NO separate `revokeToken` property. Use `authState.refreshToken`
35
- * when calling `auth.revokeToken()` or just call `sdk.logout()`.
36
+ * Note: Use `sdk.logout()` to revoke the token and clean up, or call
37
+ * `sdk.auth.revokeToken()` directly to just revoke.
36
38
  *
37
39
  * @param clientId - The client ID for authentication
38
40
  * @param options - Optional configuration
@@ -50,7 +52,7 @@ export declare class MonacoSDKImpl implements MonacoSDK {
50
52
  * await sdk.ws.connect();
51
53
  *
52
54
  * // Later, to revoke:
53
- * await sdk.auth.revokeToken(authState.refreshToken); // ✅
55
+ * await sdk.auth.revokeToken(); // ✅
54
56
  * // Or revoke and disconnect WebSocket:
55
57
  * await sdk.logout(); // ✅ Calls revokeToken internally and disconnects WebSocket
56
58
  * ```
@@ -76,7 +78,7 @@ export declare class MonacoSDKImpl implements MonacoSDK {
76
78
  *
77
79
  * This method revokes the refresh token (if available), disconnects all authenticated
78
80
  * WebSocket channels, and clears the local auth state.
79
- * It internally calls `auth.revokeToken(refreshToken)` to invalidate the token on the server.
81
+ * It internally calls `auth.revokeToken()` to invalidate the token on the server.
80
82
  *
81
83
  * @example
82
84
  * ```typescript
package/dist/sdk.js CHANGED
@@ -4,7 +4,9 @@ import { sei, seiTestnet } from "viem/chains";
4
4
  import { ApplicationsAPIImpl, createMonacoWebSocket, OrderbookAPIImpl, TradesAPIImpl } from "./api";
5
5
  import { AuthAPIImpl } from "./api/auth";
6
6
  import { FeesAPIImpl } from "./api/fees";
7
+ import { MarginAccountsAPIImpl } from "./api/margin-accounts";
7
8
  import { MarketAPIImpl } from "./api/market";
9
+ import { PositionsAPIImpl } from "./api/positions";
8
10
  import { ProfileAPIImpl } from "./api/profile";
9
11
  import { TradingAPIImpl } from "./api/trading";
10
12
  import { VaultAPIImpl } from "./api/vault";
@@ -17,6 +19,8 @@ export class MonacoSDKImpl {
17
19
  vault;
18
20
  trading;
19
21
  market;
22
+ marginAccounts;
23
+ positions;
20
24
  profile;
21
25
  orderbook;
22
26
  trades;
@@ -36,7 +40,11 @@ export class MonacoSDKImpl {
36
40
  this.vault.setAccessToken(accessToken);
37
41
  this.trading.setAccessToken(accessToken);
38
42
  this.market.setAccessToken(accessToken);
43
+ this.marginAccounts.setAccessToken(accessToken);
44
+ this.positions.setAccessToken(accessToken);
39
45
  this.profile.setAccessToken(accessToken);
46
+ this.orderbook.setAccessToken(accessToken);
47
+ this.trades.setAccessToken(accessToken);
40
48
  this.ws.setToken(accessToken);
41
49
  }
42
50
  constructor(cfg) {
@@ -79,6 +87,8 @@ export class MonacoSDKImpl {
79
87
  // Instantiate APIs (wallet-dependent APIs will be initialized lazily or error if wallet not set)
80
88
  this.applications = new ApplicationsAPIImpl(apiUrl);
81
89
  this.market = new MarketAPIImpl(apiUrl);
90
+ this.marginAccounts = new MarginAccountsAPIImpl(apiUrl);
91
+ this.positions = new PositionsAPIImpl(apiUrl);
82
92
  this.auth = new AuthAPIImpl(this.walletClient, this.chain, apiUrl);
83
93
  this.fees = new FeesAPIImpl(apiUrl);
84
94
  this.profile = new ProfileAPIImpl(apiUrl);
@@ -99,8 +109,8 @@ export class MonacoSDKImpl {
99
109
  * - `expiresAt`: When the access token expires
100
110
  * - `user`: User information
101
111
  *
102
- * Note: There is NO separate `revokeToken` property. Use `authState.refreshToken`
103
- * when calling `auth.revokeToken()` or just call `sdk.logout()`.
112
+ * Note: Use `sdk.logout()` to revoke the token and clean up, or call
113
+ * `sdk.auth.revokeToken()` directly to just revoke.
104
114
  *
105
115
  * @param clientId - The client ID for authentication
106
116
  * @param options - Optional configuration
@@ -118,7 +128,7 @@ export class MonacoSDKImpl {
118
128
  * await sdk.ws.connect();
119
129
  *
120
130
  * // Later, to revoke:
121
- * await sdk.auth.revokeToken(authState.refreshToken); // ✅
131
+ * await sdk.auth.revokeToken(); // ✅
122
132
  * // Or revoke and disconnect WebSocket:
123
133
  * await sdk.logout(); // ✅ Calls revokeToken internally and disconnects WebSocket
124
134
  * ```
@@ -161,7 +171,7 @@ export class MonacoSDKImpl {
161
171
  *
162
172
  * This method revokes the refresh token (if available), disconnects all authenticated
163
173
  * WebSocket channels, and clears the local auth state.
164
- * It internally calls `auth.revokeToken(refreshToken)` to invalidate the token on the server.
174
+ * It internally calls `auth.revokeToken()` to invalidate the token on the server.
165
175
  *
166
176
  * @example
167
177
  * ```typescript
@@ -172,18 +182,16 @@ export class MonacoSDKImpl {
172
182
  async logout() {
173
183
  if (this.authState?.refreshToken) {
174
184
  try {
175
- await this.auth.revokeToken(this.authState.refreshToken);
185
+ await this.auth.revokeToken();
176
186
  }
177
187
  catch (error) {
178
188
  // Log but don't throw - we want to clear the local state regardless
179
189
  console.warn("Failed to revoke token on logout:", error);
180
190
  }
181
191
  }
182
- // Disconnect WebSocket if connected
183
- if (this.ws.isConnected()) {
184
- this.ws.disconnect();
185
- }
186
192
  this.authState = undefined;
193
+ this.propagateAccessToken("");
194
+ this.ws.disconnect();
187
195
  }
188
196
  /**
189
197
  * Refresh the access token