@0xmonaco/core 0.6.3 → 0.7.1
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.
- package/dist/api/index.d.ts +3 -0
- package/dist/api/index.js +3 -0
- package/dist/api/margin-accounts/api.d.ts +12 -0
- package/dist/api/margin-accounts/api.js +69 -0
- package/dist/api/margin-accounts/index.d.ts +1 -0
- package/dist/api/margin-accounts/index.js +1 -0
- package/dist/api/market/api.d.ts +8 -1
- package/dist/api/market/api.js +34 -6
- package/dist/api/orderbook/api.js +1 -1
- package/dist/api/perp/index.d.ts +1 -0
- package/dist/api/perp/index.js +1 -0
- package/dist/api/perp/routes.d.ts +133 -0
- package/dist/api/perp/routes.js +85 -0
- package/dist/api/positions/api.d.ts +12 -0
- package/dist/api/positions/api.js +89 -0
- package/dist/api/positions/index.d.ts +1 -0
- package/dist/api/positions/index.js +1 -0
- package/dist/api/profile/api.d.ts +10 -10
- package/dist/api/profile/api.js +18 -18
- package/dist/api/trades/api.d.ts +4 -4
- package/dist/api/trades/api.js +8 -8
- package/dist/api/trading/api.d.ts +14 -3
- package/dist/api/trading/api.js +76 -15
- package/dist/api/websocket/types.d.ts +3 -1
- package/dist/api/websocket/websocket.js +153 -17
- package/dist/sdk.d.ts +3 -1
- package/dist/sdk.js +12 -4
- package/package.json +1 -1
|
@@ -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(() =>
|
|
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
|
-
|
|
196
|
+
const socket = new WebSocket(getUrl());
|
|
197
|
+
ws = socket;
|
|
100
198
|
const timeout = setTimeout(() => {
|
|
101
|
-
if (ws
|
|
102
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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;
|
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);
|
|
@@ -179,11 +189,9 @@ export class MonacoSDKImpl {
|
|
|
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
|