@pear-protocol/hyperliquid-sdk 0.0.3 → 0.0.5
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/hooks/useTrading.d.ts +19 -6
- package/dist/hyperliquid-service.d.ts +75 -0
- package/dist/index.d.ts +118 -7
- package/dist/index.esm.js +3083 -31
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +3083 -29
- package/dist/index.js.map +1 -1
- package/dist/migration-sdk.d.ts +16 -2
- package/dist/provider.d.ts +11 -0
- package/package.json +11 -3
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import require$$0, { createContext, useMemo, useState, useEffect, useContext } from 'react';
|
|
2
|
+
import require$$0, { createContext, useMemo, useState, useEffect, useContext, useCallback } from 'react';
|
|
3
3
|
import require$$1 from 'react-dom';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -94,7 +94,9 @@ class PearHyperliquidClient {
|
|
|
94
94
|
*/
|
|
95
95
|
class PearMigrationSDK {
|
|
96
96
|
constructor(client) {
|
|
97
|
-
this.
|
|
97
|
+
this.isTradeHistorySyncRunning = false;
|
|
98
|
+
this.isOpenPositionsSyncRunning = false;
|
|
99
|
+
this.isOpenOrdersSyncRunning = false;
|
|
98
100
|
this.client = client;
|
|
99
101
|
}
|
|
100
102
|
/**
|
|
@@ -103,16 +105,16 @@ class PearMigrationSDK {
|
|
|
103
105
|
*/
|
|
104
106
|
async syncTradeHistory(payload) {
|
|
105
107
|
// If sync is already running, return immediately
|
|
106
|
-
if (this.
|
|
108
|
+
if (this.isTradeHistorySyncRunning) {
|
|
107
109
|
return null;
|
|
108
110
|
}
|
|
109
111
|
try {
|
|
110
|
-
this.
|
|
112
|
+
this.isTradeHistorySyncRunning = true;
|
|
111
113
|
const response = await this.client.syncTradeHistory(payload);
|
|
112
114
|
return response;
|
|
113
115
|
}
|
|
114
116
|
finally {
|
|
115
|
-
this.
|
|
117
|
+
this.isTradeHistorySyncRunning = false;
|
|
116
118
|
}
|
|
117
119
|
}
|
|
118
120
|
/**
|
|
@@ -121,16 +123,16 @@ class PearMigrationSDK {
|
|
|
121
123
|
*/
|
|
122
124
|
async syncOpenPositions(payload) {
|
|
123
125
|
// If sync is already running, return immediately
|
|
124
|
-
if (this.
|
|
126
|
+
if (this.isOpenPositionsSyncRunning) {
|
|
125
127
|
return null;
|
|
126
128
|
}
|
|
127
129
|
try {
|
|
128
|
-
this.
|
|
130
|
+
this.isOpenPositionsSyncRunning = true;
|
|
129
131
|
const response = await this.client.syncOpenPositions(payload);
|
|
130
132
|
return response;
|
|
131
133
|
}
|
|
132
134
|
finally {
|
|
133
|
-
this.
|
|
135
|
+
this.isOpenPositionsSyncRunning = false;
|
|
134
136
|
}
|
|
135
137
|
}
|
|
136
138
|
/**
|
|
@@ -139,47 +141,2949 @@ class PearMigrationSDK {
|
|
|
139
141
|
*/
|
|
140
142
|
async syncOpenOrders(payload) {
|
|
141
143
|
// If sync is already running, return immediately
|
|
142
|
-
if (this.
|
|
144
|
+
if (this.isOpenOrdersSyncRunning) {
|
|
143
145
|
return null;
|
|
144
146
|
}
|
|
145
147
|
try {
|
|
146
|
-
this.
|
|
148
|
+
this.isOpenOrdersSyncRunning = true;
|
|
147
149
|
const response = await this.client.syncOpenOrders(payload);
|
|
148
150
|
return response;
|
|
149
151
|
}
|
|
150
152
|
finally {
|
|
151
|
-
this.
|
|
153
|
+
this.isOpenOrdersSyncRunning = false;
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
156
|
/**
|
|
155
|
-
* Check if sync is currently running
|
|
157
|
+
* Check if any sync is currently running
|
|
156
158
|
*/
|
|
157
159
|
isSyncInProgress() {
|
|
158
|
-
return this.
|
|
160
|
+
return this.isTradeHistorySyncRunning || this.isOpenPositionsSyncRunning || this.isOpenOrdersSyncRunning;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Check if trade history sync is currently running
|
|
164
|
+
*/
|
|
165
|
+
isTradeHistorySyncInProgress() {
|
|
166
|
+
return this.isTradeHistorySyncRunning;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Check if open positions sync is currently running
|
|
170
|
+
*/
|
|
171
|
+
isOpenPositionsSyncInProgress() {
|
|
172
|
+
return this.isOpenPositionsSyncRunning;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Check if open orders sync is currently running
|
|
176
|
+
*/
|
|
177
|
+
isOpenOrdersSyncInProgress() {
|
|
178
|
+
return this.isOpenOrdersSyncRunning;
|
|
159
179
|
}
|
|
160
180
|
/**
|
|
161
181
|
* Get the underlying client instance
|
|
162
182
|
*/
|
|
163
|
-
getClient() {
|
|
164
|
-
return this.client;
|
|
183
|
+
getClient() {
|
|
184
|
+
return this.client;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Set authorization token on the client
|
|
188
|
+
*/
|
|
189
|
+
setAuthToken(token) {
|
|
190
|
+
this.client.setAuthToken(token);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Set custom headers on the client
|
|
194
|
+
*/
|
|
195
|
+
setHeaders(headers) {
|
|
196
|
+
this.client.setHeaders(headers);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get base URL from client
|
|
200
|
+
*/
|
|
201
|
+
getBaseUrl() {
|
|
202
|
+
return this.client.getBaseUrl();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Base error class for all SDK errors. */
|
|
207
|
+
class HyperliquidError extends Error {
|
|
208
|
+
constructor(message) {
|
|
209
|
+
super(message);
|
|
210
|
+
this.name = "HyperliquidError";
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Base class for all transport-related errors. */
|
|
215
|
+
class TransportError extends HyperliquidError {
|
|
216
|
+
constructor(message) {
|
|
217
|
+
super(message);
|
|
218
|
+
this.name = "TransportError";
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Info client for interacting with the Hyperliquid API.
|
|
224
|
+
* @typeParam T The type of transport used to connect to the Hyperliquid API.
|
|
225
|
+
*/
|
|
226
|
+
class InfoClient {
|
|
227
|
+
transport;
|
|
228
|
+
/**
|
|
229
|
+
* Initialises a new instance.
|
|
230
|
+
* @param args - The arguments for initialisation.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```ts
|
|
234
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
235
|
+
*
|
|
236
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
237
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
constructor(args) {
|
|
241
|
+
this.transport = args.transport;
|
|
242
|
+
}
|
|
243
|
+
allMids(args_or_signal, maybeSignal) {
|
|
244
|
+
const args = args_or_signal instanceof AbortSignal ? {} : args_or_signal;
|
|
245
|
+
const signal = args_or_signal instanceof AbortSignal ? args_or_signal : maybeSignal;
|
|
246
|
+
const request = {
|
|
247
|
+
type: "allMids",
|
|
248
|
+
...args,
|
|
249
|
+
};
|
|
250
|
+
return this.transport.request("info", request, signal);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Block details by block height.
|
|
254
|
+
* @param args - The parameters for the request.
|
|
255
|
+
* @param signal - An optional abort signal.
|
|
256
|
+
* @returns Block details response.
|
|
257
|
+
*
|
|
258
|
+
* @see null - no documentation
|
|
259
|
+
* @example
|
|
260
|
+
* ```ts
|
|
261
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
262
|
+
*
|
|
263
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
264
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
265
|
+
*
|
|
266
|
+
* const data = await infoClient.blockDetails({ height: 123 });
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
async blockDetails(args, signal) {
|
|
270
|
+
const request = {
|
|
271
|
+
type: "blockDetails",
|
|
272
|
+
...args,
|
|
273
|
+
};
|
|
274
|
+
const { blockDetails } = await this.transport.request("explorer", request, signal);
|
|
275
|
+
return blockDetails;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Request candlestick snapshots.
|
|
279
|
+
* @param args - The parameters for the request.
|
|
280
|
+
* @param signal - An optional abort signal.
|
|
281
|
+
* @returns Array of candlestick data points.
|
|
282
|
+
*
|
|
283
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#candle-snapshot
|
|
284
|
+
* @example
|
|
285
|
+
* ```ts
|
|
286
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
287
|
+
*
|
|
288
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
289
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
290
|
+
*
|
|
291
|
+
* const data = await infoClient.candleSnapshot({
|
|
292
|
+
* coin: "ETH",
|
|
293
|
+
* interval: "1h",
|
|
294
|
+
* startTime: Date.now() - 1000 * 60 * 60 * 24
|
|
295
|
+
* });
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
candleSnapshot(args, signal) {
|
|
299
|
+
const request = {
|
|
300
|
+
type: "candleSnapshot",
|
|
301
|
+
req: args,
|
|
302
|
+
};
|
|
303
|
+
return this.transport.request("info", request, signal);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Request clearinghouse state.
|
|
307
|
+
* @param args - The parameters for the request.
|
|
308
|
+
* @param signal - An optional abort signal.
|
|
309
|
+
* @returns Account summary for perpetual trading.
|
|
310
|
+
*
|
|
311
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary
|
|
312
|
+
* @example
|
|
313
|
+
* ```ts
|
|
314
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
315
|
+
*
|
|
316
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
317
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
318
|
+
*
|
|
319
|
+
* const data = await infoClient.clearinghouseState({ user: "0x..." });
|
|
320
|
+
* ```
|
|
321
|
+
*/
|
|
322
|
+
clearinghouseState(args, signal) {
|
|
323
|
+
const request = {
|
|
324
|
+
type: "clearinghouseState",
|
|
325
|
+
...args,
|
|
326
|
+
};
|
|
327
|
+
return this.transport.request("info", request, signal);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Request user staking delegations.
|
|
331
|
+
* @param args - The parameters for the request.
|
|
332
|
+
* @param signal - An optional abort signal.
|
|
333
|
+
* @returns Array of user's delegations to validators.
|
|
334
|
+
*
|
|
335
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-a-users-staking-delegations
|
|
336
|
+
* @example
|
|
337
|
+
* ```ts
|
|
338
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
339
|
+
*
|
|
340
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
341
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
342
|
+
*
|
|
343
|
+
* const data = await infoClient.delegations({ user: "0x..." });
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
delegations(args, signal) {
|
|
347
|
+
const request = {
|
|
348
|
+
type: "delegations",
|
|
349
|
+
...args,
|
|
350
|
+
};
|
|
351
|
+
return this.transport.request("info", request, signal);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Request user staking history.
|
|
355
|
+
* @param args - The parameters for the request.
|
|
356
|
+
* @param signal - An optional abort signal.
|
|
357
|
+
* @returns Array of user's staking updates.
|
|
358
|
+
*
|
|
359
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-a-users-staking-history
|
|
360
|
+
* @example
|
|
361
|
+
* ```ts
|
|
362
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
363
|
+
*
|
|
364
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
365
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
366
|
+
*
|
|
367
|
+
* const data = await infoClient.delegatorHistory({ user: "0x..." });
|
|
368
|
+
* ```
|
|
369
|
+
*/
|
|
370
|
+
delegatorHistory(args, signal) {
|
|
371
|
+
const request = {
|
|
372
|
+
type: "delegatorHistory",
|
|
373
|
+
...args,
|
|
374
|
+
};
|
|
375
|
+
return this.transport.request("info", request, signal);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Request user staking rewards.
|
|
379
|
+
* @param args - The parameters for the request.
|
|
380
|
+
* @param signal - An optional abort signal.
|
|
381
|
+
* @returns Array of user's staking rewards.
|
|
382
|
+
*
|
|
383
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-a-users-staking-rewards
|
|
384
|
+
* @example
|
|
385
|
+
* ```ts
|
|
386
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
387
|
+
*
|
|
388
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
389
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
390
|
+
*
|
|
391
|
+
* const data = await infoClient.delegatorRewards({ user: "0x..." });
|
|
392
|
+
* ```
|
|
393
|
+
*/
|
|
394
|
+
delegatorRewards(args, signal) {
|
|
395
|
+
const request = {
|
|
396
|
+
type: "delegatorRewards",
|
|
397
|
+
...args,
|
|
398
|
+
};
|
|
399
|
+
return this.transport.request("info", request, signal);
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Request user staking summary.
|
|
403
|
+
* @param args - The parameters for the request.
|
|
404
|
+
* @param signal - An optional abort signal.
|
|
405
|
+
* @returns Summary of a user's staking delegations.
|
|
406
|
+
*
|
|
407
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-a-users-staking-summary
|
|
408
|
+
* @example
|
|
409
|
+
* ```ts
|
|
410
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
411
|
+
*
|
|
412
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
413
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
414
|
+
*
|
|
415
|
+
* const data = await infoClient.delegatorSummary({ user: "0x..." });
|
|
416
|
+
* ```
|
|
417
|
+
*/
|
|
418
|
+
delegatorSummary(args, signal) {
|
|
419
|
+
const request = {
|
|
420
|
+
type: "delegatorSummary",
|
|
421
|
+
...args,
|
|
422
|
+
};
|
|
423
|
+
return this.transport.request("info", request, signal);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Request user's extra agents.
|
|
427
|
+
* @param args - The parameters for the request.
|
|
428
|
+
* @param signal - An optional abort signal.
|
|
429
|
+
* @returns User's extra agents.
|
|
430
|
+
*
|
|
431
|
+
* @see null - no documentation
|
|
432
|
+
* @example
|
|
433
|
+
* ```ts
|
|
434
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
435
|
+
*
|
|
436
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
437
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
438
|
+
*
|
|
439
|
+
* const data = await infoClient.extraAgents({ user: "0x..." });
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
extraAgents(args, signal) {
|
|
443
|
+
const request = {
|
|
444
|
+
type: "extraAgents",
|
|
445
|
+
...args,
|
|
446
|
+
};
|
|
447
|
+
return this.transport.request("info", request, signal);
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Request frontend open orders.
|
|
451
|
+
* @param args - The parameters for the request.
|
|
452
|
+
* @param signal - An optional abort signal.
|
|
453
|
+
* @returns Array of open orders with additional frontend information.
|
|
454
|
+
*
|
|
455
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-open-orders-with-additional-frontend-info
|
|
456
|
+
* @example
|
|
457
|
+
* ```ts
|
|
458
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
459
|
+
*
|
|
460
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
461
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
462
|
+
*
|
|
463
|
+
* const data = await infoClient.frontendOpenOrders({ user: "0x..." });
|
|
464
|
+
* ```
|
|
465
|
+
*/
|
|
466
|
+
frontendOpenOrders(args, signal) {
|
|
467
|
+
const request = {
|
|
468
|
+
type: "frontendOpenOrders",
|
|
469
|
+
...args,
|
|
470
|
+
};
|
|
471
|
+
return this.transport.request("info", request, signal);
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Request funding history.
|
|
475
|
+
* @param args - The parameters for the request.
|
|
476
|
+
* @param signal - An optional abort signal.
|
|
477
|
+
* @returns Array of historical funding rate data for an asset.
|
|
478
|
+
*
|
|
479
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-historical-funding-rates
|
|
480
|
+
* @example
|
|
481
|
+
* ```ts
|
|
482
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
483
|
+
*
|
|
484
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
485
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
486
|
+
*
|
|
487
|
+
* const data = await infoClient.fundingHistory({
|
|
488
|
+
* coin: "ETH",
|
|
489
|
+
* startTime: Date.now() - 1000 * 60 * 60 * 24
|
|
490
|
+
* });
|
|
491
|
+
* ```
|
|
492
|
+
*/
|
|
493
|
+
fundingHistory(args, signal) {
|
|
494
|
+
const request = {
|
|
495
|
+
type: "fundingHistory",
|
|
496
|
+
...args,
|
|
497
|
+
};
|
|
498
|
+
return this.transport.request("info", request, signal);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Request user's historical orders.
|
|
502
|
+
* @param args - The parameters for the request.
|
|
503
|
+
* @param signal - An optional abort signal.
|
|
504
|
+
* @returns Array of user's historical orders.
|
|
505
|
+
*
|
|
506
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-historical-orders
|
|
507
|
+
* @example
|
|
508
|
+
* ```ts
|
|
509
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
510
|
+
*
|
|
511
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
512
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
513
|
+
*
|
|
514
|
+
* const data = await infoClient.historicalOrders({ user: "0x..." });
|
|
515
|
+
* ```
|
|
516
|
+
*/
|
|
517
|
+
historicalOrders(args, signal) {
|
|
518
|
+
const request = {
|
|
519
|
+
type: "historicalOrders",
|
|
520
|
+
...args,
|
|
521
|
+
};
|
|
522
|
+
return this.transport.request("info", request, signal);
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Request to check if a user is a VIP.
|
|
526
|
+
* @param args - The parameters for the request.
|
|
527
|
+
* @param signal - An optional abort signal.
|
|
528
|
+
* @returns Boolean indicating user's VIP status.
|
|
529
|
+
*
|
|
530
|
+
* @see null - no documentation
|
|
531
|
+
* @example
|
|
532
|
+
* ```ts
|
|
533
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
534
|
+
*
|
|
535
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
536
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
537
|
+
*
|
|
538
|
+
* const data = await infoClient.isVip({ user: "0x..." });
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
541
|
+
isVip(args, signal) {
|
|
542
|
+
const request = {
|
|
543
|
+
type: "isVip",
|
|
544
|
+
...args,
|
|
545
|
+
};
|
|
546
|
+
return this.transport.request("info", request, signal);
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Request L2 order book.
|
|
550
|
+
* @param args - The parameters for the request.
|
|
551
|
+
* @param signal - An optional abort signal.
|
|
552
|
+
* @returns L2 order book snapshot.
|
|
553
|
+
*
|
|
554
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#l2-book-snapshot
|
|
555
|
+
* @example
|
|
556
|
+
* ```ts
|
|
557
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
558
|
+
*
|
|
559
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
560
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
561
|
+
*
|
|
562
|
+
* const data = await infoClient.l2Book({ coin: "ETH", nSigFigs: 2 });
|
|
563
|
+
* ```
|
|
564
|
+
*/
|
|
565
|
+
l2Book(args, signal) {
|
|
566
|
+
const request = {
|
|
567
|
+
type: "l2Book",
|
|
568
|
+
...args,
|
|
569
|
+
};
|
|
570
|
+
return this.transport.request("info", request, signal);
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Request legal verification status of a user.
|
|
574
|
+
* @param args - The parameters for the request.
|
|
575
|
+
* @param signal - An optional abort signal.
|
|
576
|
+
* @returns Legal verification status for a user.
|
|
577
|
+
*
|
|
578
|
+
* @see null - no documentation
|
|
579
|
+
* @example
|
|
580
|
+
* ```ts
|
|
581
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
582
|
+
*
|
|
583
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
584
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
585
|
+
*
|
|
586
|
+
* const data = await infoClient.legalCheck({ user: "0x..." });
|
|
587
|
+
* ```
|
|
588
|
+
*/
|
|
589
|
+
legalCheck(args, signal) {
|
|
590
|
+
const request = {
|
|
591
|
+
type: "legalCheck",
|
|
592
|
+
...args,
|
|
593
|
+
};
|
|
594
|
+
return this.transport.request("info", request, signal);
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Request builder fee approval.
|
|
598
|
+
* @param args - The parameters for the request.
|
|
599
|
+
* @param signal - An optional abort signal.
|
|
600
|
+
* @returns Maximum builder fee approval.
|
|
601
|
+
*
|
|
602
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#check-builder-fee-approval
|
|
603
|
+
* @example
|
|
604
|
+
* ```ts
|
|
605
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
606
|
+
*
|
|
607
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
608
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
609
|
+
*
|
|
610
|
+
* const data = await infoClient.maxBuilderFee({ user: "0x...", builder: "0x..." });
|
|
611
|
+
* ```
|
|
612
|
+
*/
|
|
613
|
+
maxBuilderFee(args, signal) {
|
|
614
|
+
const request = {
|
|
615
|
+
type: "maxBuilderFee",
|
|
616
|
+
...args,
|
|
617
|
+
};
|
|
618
|
+
return this.transport.request("info", request, signal);
|
|
619
|
+
}
|
|
620
|
+
meta(args_or_signal, maybeSignal) {
|
|
621
|
+
const args = args_or_signal instanceof AbortSignal ? {} : args_or_signal;
|
|
622
|
+
const signal = args_or_signal instanceof AbortSignal ? args_or_signal : maybeSignal;
|
|
623
|
+
const request = {
|
|
624
|
+
type: "meta",
|
|
625
|
+
...args,
|
|
626
|
+
};
|
|
627
|
+
return this.transport.request("info", request, signal);
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Request metadata and asset contexts.
|
|
631
|
+
* @param signal - An optional abort signal.
|
|
632
|
+
* @returns Metadata and context information for each perpetual asset.
|
|
633
|
+
*
|
|
634
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
|
|
635
|
+
* @example
|
|
636
|
+
* ```ts
|
|
637
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
638
|
+
*
|
|
639
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
640
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
641
|
+
*
|
|
642
|
+
* const data = await infoClient.metaAndAssetCtxs();
|
|
643
|
+
* ```
|
|
644
|
+
*/
|
|
645
|
+
metaAndAssetCtxs(signal) {
|
|
646
|
+
const request = {
|
|
647
|
+
type: "metaAndAssetCtxs",
|
|
648
|
+
};
|
|
649
|
+
return this.transport.request("info", request, signal);
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Request open orders.
|
|
653
|
+
* @param args - The parameters for the request.
|
|
654
|
+
* @param signal - An optional abort signal.
|
|
655
|
+
* @returns Array of open order.
|
|
656
|
+
*
|
|
657
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-open-orders
|
|
658
|
+
* @example
|
|
659
|
+
* ```ts
|
|
660
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
661
|
+
*
|
|
662
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
663
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
664
|
+
*
|
|
665
|
+
* const data = await infoClient.openOrders({ user: "0x..." });
|
|
666
|
+
* ```
|
|
667
|
+
*/
|
|
668
|
+
openOrders(args, signal) {
|
|
669
|
+
const request = {
|
|
670
|
+
type: "openOrders",
|
|
671
|
+
...args,
|
|
672
|
+
};
|
|
673
|
+
return this.transport.request("info", request, signal);
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Request order status.
|
|
677
|
+
* @param args - The parameters for the request.
|
|
678
|
+
* @param signal - An optional abort signal.
|
|
679
|
+
* @returns Result of an order status lookup.
|
|
680
|
+
*
|
|
681
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-order-status-by-oid-or-cloid
|
|
682
|
+
* @example
|
|
683
|
+
* ```ts
|
|
684
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
685
|
+
*
|
|
686
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
687
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
688
|
+
*
|
|
689
|
+
* const data = await infoClient.orderStatus({ user: "0x...", oid: 12345 });
|
|
690
|
+
* ```
|
|
691
|
+
*/
|
|
692
|
+
orderStatus(args, signal) {
|
|
693
|
+
const request = {
|
|
694
|
+
type: "orderStatus",
|
|
695
|
+
...args,
|
|
696
|
+
};
|
|
697
|
+
return this.transport.request("info", request, signal);
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Request for the status of the perpetual deploy auction.
|
|
701
|
+
* @param signal - An optional abort signal.
|
|
702
|
+
* @returns Status of the perpetual deploy auction.
|
|
703
|
+
*
|
|
704
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-information-about-the-perp-deploy-auction
|
|
705
|
+
* @example
|
|
706
|
+
* ```ts
|
|
707
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
708
|
+
*
|
|
709
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
710
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
711
|
+
*
|
|
712
|
+
* const data = await infoClient.perpDeployAuctionStatus();
|
|
713
|
+
* ```
|
|
714
|
+
*/
|
|
715
|
+
perpDeployAuctionStatus(signal) {
|
|
716
|
+
const request = {
|
|
717
|
+
type: "perpDeployAuctionStatus",
|
|
718
|
+
};
|
|
719
|
+
return this.transport.request("info", request, signal);
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Request all perpetual dexs.
|
|
723
|
+
* @param signal - An optional abort signal.
|
|
724
|
+
* @returns Array of perpetual dexes.
|
|
725
|
+
*
|
|
726
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-all-perpetual-dexs
|
|
727
|
+
* @example
|
|
728
|
+
* ```ts
|
|
729
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
730
|
+
*
|
|
731
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
732
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
733
|
+
*
|
|
734
|
+
* const data = await infoClient.perpDexs();
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
perpDexs(signal) {
|
|
738
|
+
const request = {
|
|
739
|
+
type: "perpDexs",
|
|
740
|
+
};
|
|
741
|
+
return this.transport.request("info", request, signal);
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Request perpetuals at open interest cap.
|
|
745
|
+
* @param args - The parameters for the request.
|
|
746
|
+
* @param signal - An optional abort signal.
|
|
747
|
+
* @returns Array of perpetuals at open interest caps.
|
|
748
|
+
*
|
|
749
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#query-perps-at-open-interest-caps
|
|
750
|
+
* @example
|
|
751
|
+
* ```ts
|
|
752
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
753
|
+
*
|
|
754
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
755
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
756
|
+
*
|
|
757
|
+
* const data = await infoClient.perpsAtOpenInterestCap();
|
|
758
|
+
* ```
|
|
759
|
+
*/
|
|
760
|
+
perpsAtOpenInterestCap(signal) {
|
|
761
|
+
const request = {
|
|
762
|
+
type: "perpsAtOpenInterestCap",
|
|
763
|
+
};
|
|
764
|
+
return this.transport.request("info", request, signal);
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Request portfolio.
|
|
768
|
+
* @param args - The parameters for the request.
|
|
769
|
+
* @param signal - An optional abort signal.
|
|
770
|
+
* @returns Portfolio of a user.
|
|
771
|
+
*
|
|
772
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-a-users-portfolio
|
|
773
|
+
* @example
|
|
774
|
+
* ```ts
|
|
775
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
776
|
+
*
|
|
777
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
778
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
779
|
+
*
|
|
780
|
+
* const data = await infoClient.portfolio({ user: "0x..." });
|
|
781
|
+
* ```
|
|
782
|
+
*/
|
|
783
|
+
portfolio(args, signal) {
|
|
784
|
+
const request = {
|
|
785
|
+
type: "portfolio",
|
|
786
|
+
...args,
|
|
787
|
+
};
|
|
788
|
+
return this.transport.request("info", request, signal);
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Request predicted funding rates.
|
|
792
|
+
* @param signal - An optional abort signal.
|
|
793
|
+
* @returns Array of predicted funding rates.
|
|
794
|
+
*
|
|
795
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-predicted-funding-rates-for-different-venues
|
|
796
|
+
* @example
|
|
797
|
+
* ```ts
|
|
798
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
799
|
+
*
|
|
800
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
801
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
802
|
+
*
|
|
803
|
+
* const data = await infoClient.predictedFundings();
|
|
804
|
+
* ```
|
|
805
|
+
*/
|
|
806
|
+
predictedFundings(signal) {
|
|
807
|
+
const request = {
|
|
808
|
+
type: "predictedFundings",
|
|
809
|
+
};
|
|
810
|
+
return this.transport.request("info", request, signal);
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Request user's existence check before transfer.
|
|
814
|
+
* @param args - The parameters for the request.
|
|
815
|
+
* @param signal - An optional abort signal.
|
|
816
|
+
* @returns Pre-transfer user existence check result.
|
|
817
|
+
*
|
|
818
|
+
* @see null - no documentation
|
|
819
|
+
* @example
|
|
820
|
+
* ```ts
|
|
821
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
822
|
+
*
|
|
823
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
824
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
825
|
+
*
|
|
826
|
+
* const data = await infoClient.preTransferCheck({ user: "0x...", source: "0x..." });
|
|
827
|
+
* ```
|
|
828
|
+
*/
|
|
829
|
+
preTransferCheck(args, signal) {
|
|
830
|
+
const request = {
|
|
831
|
+
type: "preTransferCheck",
|
|
832
|
+
...args,
|
|
833
|
+
};
|
|
834
|
+
return this.transport.request("info", request, signal);
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Request user referral.
|
|
838
|
+
* @param args - The parameters for the request.
|
|
839
|
+
* @param signal - An optional abort signal.
|
|
840
|
+
* @returns Referral information for a user.
|
|
841
|
+
*
|
|
842
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-a-users-referral-information
|
|
843
|
+
* @example
|
|
844
|
+
* ```ts
|
|
845
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
846
|
+
*
|
|
847
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
848
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
849
|
+
*
|
|
850
|
+
* const data = await infoClient.referral({ user: "0x..." });
|
|
851
|
+
* ```
|
|
852
|
+
*/
|
|
853
|
+
referral(args, signal) {
|
|
854
|
+
const request = {
|
|
855
|
+
type: "referral",
|
|
856
|
+
...args,
|
|
857
|
+
};
|
|
858
|
+
return this.transport.request("info", request, signal);
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Request spot clearinghouse state.
|
|
862
|
+
* @param args - The parameters for the request.
|
|
863
|
+
* @param signal - An optional abort signal.
|
|
864
|
+
* @returns Balances for spot tokens.
|
|
865
|
+
*
|
|
866
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-a-users-token-balances
|
|
867
|
+
* @example
|
|
868
|
+
* ```ts
|
|
869
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
870
|
+
*
|
|
871
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
872
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
873
|
+
*
|
|
874
|
+
* const data = await infoClient.spotClearinghouseState({ user: "0x..." });
|
|
875
|
+
* ```
|
|
876
|
+
*/
|
|
877
|
+
spotClearinghouseState(args, signal) {
|
|
878
|
+
const request = {
|
|
879
|
+
type: "spotClearinghouseState",
|
|
880
|
+
...args,
|
|
881
|
+
};
|
|
882
|
+
return this.transport.request("info", request, signal);
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Request spot deploy state.
|
|
886
|
+
* @param args - The parameters for the request.
|
|
887
|
+
* @param signal - An optional abort signal.
|
|
888
|
+
* @returns The deploy state of a user.
|
|
889
|
+
*
|
|
890
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-information-about-the-spot-deploy-auction
|
|
891
|
+
* @example
|
|
892
|
+
* ```ts
|
|
893
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
894
|
+
*
|
|
895
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
896
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
897
|
+
*
|
|
898
|
+
* const data = await infoClient.spotDeployState({ user: "0x..." });
|
|
899
|
+
* ```
|
|
900
|
+
*/
|
|
901
|
+
spotDeployState(args, signal) {
|
|
902
|
+
const request = {
|
|
903
|
+
type: "spotDeployState",
|
|
904
|
+
...args,
|
|
905
|
+
};
|
|
906
|
+
return this.transport.request("info", request, signal);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Request spot trading metadata.
|
|
910
|
+
* @param signal - An optional abort signal.
|
|
911
|
+
* @returns Metadata for spot assets.
|
|
912
|
+
*
|
|
913
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-metadata
|
|
914
|
+
* @example
|
|
915
|
+
* ```ts
|
|
916
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
917
|
+
*
|
|
918
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
919
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
920
|
+
*
|
|
921
|
+
* const data = await infoClient.spotMeta();
|
|
922
|
+
* ```
|
|
923
|
+
*/
|
|
924
|
+
spotMeta(signal) {
|
|
925
|
+
const request = {
|
|
926
|
+
type: "spotMeta",
|
|
927
|
+
};
|
|
928
|
+
return this.transport.request("info", request, signal);
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Request spot metadata and asset contexts.
|
|
932
|
+
* @param signal - An optional abort signal.
|
|
933
|
+
* @returns Metadata and context information for each spot asset.
|
|
934
|
+
*
|
|
935
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts
|
|
936
|
+
* @example
|
|
937
|
+
* ```ts
|
|
938
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
939
|
+
*
|
|
940
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
941
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
942
|
+
*
|
|
943
|
+
* const data = await infoClient.spotMetaAndAssetCtxs();
|
|
944
|
+
* ```
|
|
945
|
+
*/
|
|
946
|
+
spotMetaAndAssetCtxs(signal) {
|
|
947
|
+
const request = {
|
|
948
|
+
type: "spotMetaAndAssetCtxs",
|
|
949
|
+
};
|
|
950
|
+
return this.transport.request("info", request, signal);
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Request user sub-accounts.
|
|
954
|
+
* @param args - The parameters for the request.
|
|
955
|
+
* @param signal - An optional abort signal.
|
|
956
|
+
* @returns Array of user sub-account or null if the user does not have any sub-accounts.
|
|
957
|
+
*
|
|
958
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-subaccounts
|
|
959
|
+
* @example
|
|
960
|
+
* ```ts
|
|
961
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
962
|
+
*
|
|
963
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
964
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
965
|
+
*
|
|
966
|
+
* const data = await infoClient.subAccounts({ user: "0x..." });
|
|
967
|
+
* ```
|
|
968
|
+
*/
|
|
969
|
+
subAccounts(args, signal) {
|
|
970
|
+
const request = {
|
|
971
|
+
type: "subAccounts",
|
|
972
|
+
...args,
|
|
973
|
+
};
|
|
974
|
+
return this.transport.request("info", request, signal);
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Request token details.
|
|
978
|
+
* @param args - The parameters for the request.
|
|
979
|
+
* @param signal - An optional abort signal.
|
|
980
|
+
* @returns The details of a token.
|
|
981
|
+
*
|
|
982
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-information-about-a-token
|
|
983
|
+
* @example
|
|
984
|
+
* ```ts
|
|
985
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
986
|
+
*
|
|
987
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
988
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
989
|
+
*
|
|
990
|
+
* const data = await infoClient.tokenDetails({ tokenId: "0x..." });
|
|
991
|
+
* ```
|
|
992
|
+
*/
|
|
993
|
+
tokenDetails(args, signal) {
|
|
994
|
+
const request = {
|
|
995
|
+
type: "tokenDetails",
|
|
996
|
+
...args,
|
|
997
|
+
};
|
|
998
|
+
return this.transport.request("info", request, signal);
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Request twap history of a user.
|
|
1002
|
+
* @param args - The parameters for the request.
|
|
1003
|
+
* @param signal - An optional abort signal.
|
|
1004
|
+
* @returns The twap history of a user.
|
|
1005
|
+
*
|
|
1006
|
+
* @see null - no documentation
|
|
1007
|
+
* @example
|
|
1008
|
+
* ```ts
|
|
1009
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1010
|
+
*
|
|
1011
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1012
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1013
|
+
*
|
|
1014
|
+
* const data = await infoClient.twapHistory({ user: "0x..." });
|
|
1015
|
+
* ```
|
|
1016
|
+
*/
|
|
1017
|
+
twapHistory(args, signal) {
|
|
1018
|
+
const request = {
|
|
1019
|
+
type: "twapHistory",
|
|
1020
|
+
...args,
|
|
1021
|
+
};
|
|
1022
|
+
return this.transport.request("info", request, signal);
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Transaction details by transaction hash.
|
|
1026
|
+
* @param args - The parameters for the request.
|
|
1027
|
+
* @param signal - An optional abort signal.
|
|
1028
|
+
* @returns Transaction details response.
|
|
1029
|
+
*
|
|
1030
|
+
* @see null - no documentation
|
|
1031
|
+
* @example
|
|
1032
|
+
* ```ts
|
|
1033
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1034
|
+
*
|
|
1035
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1036
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1037
|
+
*
|
|
1038
|
+
* const data = await infoClient.txDetails({ hash: "0x..." });
|
|
1039
|
+
* ```
|
|
1040
|
+
*/
|
|
1041
|
+
async txDetails(args, signal) {
|
|
1042
|
+
const request = {
|
|
1043
|
+
type: "txDetails",
|
|
1044
|
+
...args,
|
|
1045
|
+
};
|
|
1046
|
+
const { tx } = await this.transport.request("explorer", request, signal);
|
|
1047
|
+
return tx;
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* User details by user's address.
|
|
1051
|
+
* @param args - The parameters for the request.
|
|
1052
|
+
* @param signal - An optional abort signal.
|
|
1053
|
+
* @returns User details response.
|
|
1054
|
+
*
|
|
1055
|
+
* @see null - no documentation
|
|
1056
|
+
* @example
|
|
1057
|
+
* ```ts
|
|
1058
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1059
|
+
*
|
|
1060
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1061
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1062
|
+
*
|
|
1063
|
+
* const data = await infoClient.userDetails({ user: "0x..." });
|
|
1064
|
+
* ```
|
|
1065
|
+
*/
|
|
1066
|
+
async userDetails(args, signal) {
|
|
1067
|
+
const request = {
|
|
1068
|
+
type: "userDetails",
|
|
1069
|
+
...args,
|
|
1070
|
+
};
|
|
1071
|
+
const { txs } = await this.transport.request("explorer", request, signal);
|
|
1072
|
+
return txs;
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Request user fees.
|
|
1076
|
+
* @param args - The parameters for the request.
|
|
1077
|
+
* @param signal - An optional abort signal.
|
|
1078
|
+
* @returns User fees.
|
|
1079
|
+
*
|
|
1080
|
+
* @see null - no documentation
|
|
1081
|
+
* @example
|
|
1082
|
+
* ```ts
|
|
1083
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1084
|
+
*
|
|
1085
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1086
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1087
|
+
*
|
|
1088
|
+
* const data = await infoClient.userFees({ user: "0x..." });
|
|
1089
|
+
* ```
|
|
1090
|
+
*/
|
|
1091
|
+
userFees(args, signal) {
|
|
1092
|
+
const request = {
|
|
1093
|
+
type: "userFees",
|
|
1094
|
+
...args,
|
|
1095
|
+
};
|
|
1096
|
+
return this.transport.request("info", request, signal);
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Request user fills.
|
|
1100
|
+
* @param args - The parameters for the request.
|
|
1101
|
+
* @param signal - An optional abort signal.
|
|
1102
|
+
* @returns Array of user's trade fill.
|
|
1103
|
+
*
|
|
1104
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills
|
|
1105
|
+
* @example
|
|
1106
|
+
* ```ts
|
|
1107
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1108
|
+
*
|
|
1109
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1110
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1111
|
+
*
|
|
1112
|
+
* const data = await infoClient.userFills({ user: "0x..." });
|
|
1113
|
+
* ```
|
|
1114
|
+
*/
|
|
1115
|
+
userFills(args, signal) {
|
|
1116
|
+
const request = {
|
|
1117
|
+
type: "userFills",
|
|
1118
|
+
...args,
|
|
1119
|
+
};
|
|
1120
|
+
return this.transport.request("info", request, signal);
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Request user fills by time.
|
|
1124
|
+
* @param args - The parameters for the request.
|
|
1125
|
+
* @param signal - An optional abort signal.
|
|
1126
|
+
* @returns Array of user's trade fill.
|
|
1127
|
+
*
|
|
1128
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time
|
|
1129
|
+
* @example
|
|
1130
|
+
* ```ts
|
|
1131
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1132
|
+
*
|
|
1133
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1134
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1135
|
+
*
|
|
1136
|
+
* const data = await infoClient.userFillsByTime({
|
|
1137
|
+
* user: "0x...",
|
|
1138
|
+
* startTime: Date.now() - 1000 * 60 * 60 * 24
|
|
1139
|
+
* });
|
|
1140
|
+
* ```
|
|
1141
|
+
*/
|
|
1142
|
+
userFillsByTime(args, signal) {
|
|
1143
|
+
const request = {
|
|
1144
|
+
type: "userFillsByTime",
|
|
1145
|
+
...args,
|
|
1146
|
+
};
|
|
1147
|
+
return this.transport.request("info", request, signal);
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* Request user funding.
|
|
1151
|
+
* @param args - The parameters for the request.
|
|
1152
|
+
* @param signal - An optional abort signal.
|
|
1153
|
+
* @returns Array of user's funding ledger update.
|
|
1154
|
+
*
|
|
1155
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-a-users-funding-history-or-non-funding-ledger-updates
|
|
1156
|
+
* @example
|
|
1157
|
+
* ```ts
|
|
1158
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1159
|
+
*
|
|
1160
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1161
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1162
|
+
*
|
|
1163
|
+
* const data = await infoClient.userFunding({
|
|
1164
|
+
* user: "0x...",
|
|
1165
|
+
* startTime: Date.now() - 1000 * 60 * 60 * 24
|
|
1166
|
+
* });
|
|
1167
|
+
* ```
|
|
1168
|
+
*/
|
|
1169
|
+
userFunding(args, signal) {
|
|
1170
|
+
const request = {
|
|
1171
|
+
type: "userFunding",
|
|
1172
|
+
...args,
|
|
1173
|
+
};
|
|
1174
|
+
return this.transport.request("info", request, signal);
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Request user non-funding ledger updates.
|
|
1178
|
+
* @param args - The parameters for the request.
|
|
1179
|
+
* @param signal - An optional abort signal.
|
|
1180
|
+
* @returns Array of user's non-funding ledger update.
|
|
1181
|
+
*
|
|
1182
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-a-users-funding-history-or-non-funding-ledger-updates
|
|
1183
|
+
* @example
|
|
1184
|
+
* ```ts
|
|
1185
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1186
|
+
*
|
|
1187
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1188
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1189
|
+
*
|
|
1190
|
+
* const data = await infoClient.userNonFundingLedgerUpdates({
|
|
1191
|
+
* user: "0x...",
|
|
1192
|
+
* startTime: Date.now() - 1000 * 60 * 60 * 24
|
|
1193
|
+
* });
|
|
1194
|
+
* ```
|
|
1195
|
+
*/
|
|
1196
|
+
userNonFundingLedgerUpdates(args, signal) {
|
|
1197
|
+
const request = {
|
|
1198
|
+
type: "userNonFundingLedgerUpdates",
|
|
1199
|
+
...args,
|
|
1200
|
+
};
|
|
1201
|
+
return this.transport.request("info", request, signal);
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Request user rate limits.
|
|
1205
|
+
* @param args - The parameters for the request.
|
|
1206
|
+
* @param signal - An optional abort signal.
|
|
1207
|
+
* @returns User's rate limits.
|
|
1208
|
+
*
|
|
1209
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-user-rate-limits
|
|
1210
|
+
* @example
|
|
1211
|
+
* ```ts
|
|
1212
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1213
|
+
*
|
|
1214
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1215
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1216
|
+
*
|
|
1217
|
+
* const data = await infoClient.userRateLimit({ user: "0x..." });
|
|
1218
|
+
* ```
|
|
1219
|
+
*/
|
|
1220
|
+
userRateLimit(args, signal) {
|
|
1221
|
+
const request = {
|
|
1222
|
+
type: "userRateLimit",
|
|
1223
|
+
...args,
|
|
1224
|
+
};
|
|
1225
|
+
return this.transport.request("info", request, signal);
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Request user role.
|
|
1229
|
+
* @param args - The parameters for the request.
|
|
1230
|
+
* @param signal - An optional abort signal.
|
|
1231
|
+
* @returns User's role.
|
|
1232
|
+
*
|
|
1233
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-a-users-role
|
|
1234
|
+
* @example
|
|
1235
|
+
* ```ts
|
|
1236
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1237
|
+
*
|
|
1238
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1239
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1240
|
+
*
|
|
1241
|
+
* const data = await infoClient.userRole({ user: "0x..." });
|
|
1242
|
+
* ```
|
|
1243
|
+
*/
|
|
1244
|
+
userRole(args, signal) {
|
|
1245
|
+
const request = {
|
|
1246
|
+
type: "userRole",
|
|
1247
|
+
...args,
|
|
1248
|
+
};
|
|
1249
|
+
return this.transport.request("info", request, signal);
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Request multi-sig signers for a user.
|
|
1253
|
+
* @param args - The parameters for the request.
|
|
1254
|
+
* @param signal - An optional abort signal.
|
|
1255
|
+
* @returns Multi-sig signers for a user or null if the user does not have any multi-sig signers.
|
|
1256
|
+
*
|
|
1257
|
+
* @see null - no documentation
|
|
1258
|
+
* @example
|
|
1259
|
+
* ```ts
|
|
1260
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1261
|
+
*
|
|
1262
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1263
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1264
|
+
*
|
|
1265
|
+
* const data = await infoClient.userToMultiSigSigners({ user: "0x..." });
|
|
1266
|
+
* ```
|
|
1267
|
+
*/
|
|
1268
|
+
userToMultiSigSigners(args, signal) {
|
|
1269
|
+
const request = {
|
|
1270
|
+
type: "userToMultiSigSigners",
|
|
1271
|
+
...args,
|
|
1272
|
+
};
|
|
1273
|
+
return this.transport.request("info", request, signal);
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Request user twap slice fills.
|
|
1277
|
+
* @param args - The parameters for the request.
|
|
1278
|
+
* @param signal - An optional abort signal.
|
|
1279
|
+
* @returns Array of user's twap slice fill.
|
|
1280
|
+
*
|
|
1281
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-twap-slice-fills
|
|
1282
|
+
* @example
|
|
1283
|
+
* ```ts
|
|
1284
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1285
|
+
*
|
|
1286
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1287
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1288
|
+
*
|
|
1289
|
+
* const data = await infoClient.userTwapSliceFills({ user: "0x..." });
|
|
1290
|
+
* ```
|
|
1291
|
+
*/
|
|
1292
|
+
userTwapSliceFills(args, signal) {
|
|
1293
|
+
const request = {
|
|
1294
|
+
type: "userTwapSliceFills",
|
|
1295
|
+
...args,
|
|
1296
|
+
};
|
|
1297
|
+
return this.transport.request("info", request, signal);
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Request user twap slice fills by time.
|
|
1301
|
+
* @param args - The parameters for the request.
|
|
1302
|
+
* @param signal - An optional abort signal.
|
|
1303
|
+
* @returns Array of user's twap slice fill.
|
|
1304
|
+
*
|
|
1305
|
+
* @see null - no documentation
|
|
1306
|
+
* @example
|
|
1307
|
+
* ```ts
|
|
1308
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1309
|
+
*
|
|
1310
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1311
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1312
|
+
*
|
|
1313
|
+
* const data = await infoClient.userTwapSliceFillsByTime({
|
|
1314
|
+
* user: "0x...",
|
|
1315
|
+
* startTime: Date.now() - 1000 * 60 * 60 * 24
|
|
1316
|
+
* });
|
|
1317
|
+
* ```
|
|
1318
|
+
*/
|
|
1319
|
+
userTwapSliceFillsByTime(args, signal) {
|
|
1320
|
+
const request = {
|
|
1321
|
+
type: "userTwapSliceFillsByTime",
|
|
1322
|
+
...args,
|
|
1323
|
+
};
|
|
1324
|
+
return this.transport.request("info", request, signal);
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Request user vault deposits.
|
|
1328
|
+
* @param args - The parameters for the request.
|
|
1329
|
+
* @param signal - An optional abort signal.
|
|
1330
|
+
* @returns Array of user's vault deposits.
|
|
1331
|
+
*
|
|
1332
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-vault-deposits
|
|
1333
|
+
* @example
|
|
1334
|
+
* ```ts
|
|
1335
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1336
|
+
*
|
|
1337
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1338
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1339
|
+
*
|
|
1340
|
+
* const data = await infoClient.userVaultDeposits({ user: "0x..." });
|
|
1341
|
+
* ```
|
|
1342
|
+
*/
|
|
1343
|
+
userVaultEquities(args, signal) {
|
|
1344
|
+
const request = {
|
|
1345
|
+
type: "userVaultEquities",
|
|
1346
|
+
...args,
|
|
1347
|
+
};
|
|
1348
|
+
return this.transport.request("info", request, signal);
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Request validator summaries.
|
|
1352
|
+
* @param args - The parameters for the request.
|
|
1353
|
+
* @returns Array of validator summaries.
|
|
1354
|
+
*
|
|
1355
|
+
* @see null - no documentation
|
|
1356
|
+
* @example
|
|
1357
|
+
* ```ts
|
|
1358
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1359
|
+
*
|
|
1360
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1361
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1362
|
+
*
|
|
1363
|
+
* const data = await infoClient.validatorSummaries();
|
|
1364
|
+
* ```
|
|
1365
|
+
*/
|
|
1366
|
+
validatorSummaries(signal) {
|
|
1367
|
+
const request = {
|
|
1368
|
+
type: "validatorSummaries",
|
|
1369
|
+
};
|
|
1370
|
+
return this.transport.request("info", request, signal);
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Request details of a vault.
|
|
1374
|
+
* @param args - The parameters for the request.
|
|
1375
|
+
* @param signal - An optional abort signal.
|
|
1376
|
+
* @returns Details of a vault or null if the vault does not exist.
|
|
1377
|
+
*
|
|
1378
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-details-for-a-vault
|
|
1379
|
+
* @example
|
|
1380
|
+
* ```ts
|
|
1381
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1382
|
+
*
|
|
1383
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1384
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1385
|
+
*
|
|
1386
|
+
* const data = await infoClient.vaultDetails({ vaultAddress: "0x..." });
|
|
1387
|
+
* ```
|
|
1388
|
+
*/
|
|
1389
|
+
vaultDetails(args, signal) {
|
|
1390
|
+
const request = {
|
|
1391
|
+
type: "vaultDetails",
|
|
1392
|
+
...args,
|
|
1393
|
+
};
|
|
1394
|
+
return this.transport.request("info", request, signal);
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Request a list of vaults less than 2 hours old.
|
|
1398
|
+
* @param args - The parameters for the request.
|
|
1399
|
+
* @param signal - An optional abort signal.
|
|
1400
|
+
* @returns Array of vault summaries.
|
|
1401
|
+
*
|
|
1402
|
+
* @see null - no documentation
|
|
1403
|
+
* @example
|
|
1404
|
+
* ```ts
|
|
1405
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1406
|
+
*
|
|
1407
|
+
* const transport = new hl.HttpTransport(); // or WebSocketTransport
|
|
1408
|
+
* const infoClient = new hl.InfoClient({ transport });
|
|
1409
|
+
*
|
|
1410
|
+
* const data = await infoClient.vaultSummaries();
|
|
1411
|
+
* ```
|
|
1412
|
+
*/
|
|
1413
|
+
vaultSummaries(signal) {
|
|
1414
|
+
const request = {
|
|
1415
|
+
type: "vaultSummaries",
|
|
1416
|
+
};
|
|
1417
|
+
return this.transport.request("info", request, signal);
|
|
1418
|
+
}
|
|
1419
|
+
async [Symbol.asyncDispose]() {
|
|
1420
|
+
await this.transport[Symbol.asyncDispose]?.();
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
/**
|
|
1425
|
+
* Subscription client for subscribing to various Hyperliquid events.
|
|
1426
|
+
* @typeParam T The type of transport used to connect to the Hyperliquid Websocket API.
|
|
1427
|
+
*/
|
|
1428
|
+
class SubscriptionClient {
|
|
1429
|
+
transport;
|
|
1430
|
+
/**
|
|
1431
|
+
* Initialises a new instance.
|
|
1432
|
+
* @param args - The arguments for initialisation.
|
|
1433
|
+
*
|
|
1434
|
+
* @example
|
|
1435
|
+
* ```ts
|
|
1436
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1437
|
+
*
|
|
1438
|
+
* const transport = new hl.WebSocketTransport();
|
|
1439
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1440
|
+
* ```
|
|
1441
|
+
*/
|
|
1442
|
+
constructor(args) {
|
|
1443
|
+
this.transport = args.transport;
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Subscribe to context updates for a specific perpetual asset.
|
|
1447
|
+
* @param args - The parameters for the subscription.
|
|
1448
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1449
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1450
|
+
*
|
|
1451
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1452
|
+
* @example
|
|
1453
|
+
* ```ts
|
|
1454
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1455
|
+
*
|
|
1456
|
+
* const transport = new hl.WebSocketTransport();
|
|
1457
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1458
|
+
*
|
|
1459
|
+
* const sub = await subsClient.activeAssetCtx({ coin: "BTC" }, (data) => {
|
|
1460
|
+
* console.log(data);
|
|
1461
|
+
* });
|
|
1462
|
+
* ```
|
|
1463
|
+
*/
|
|
1464
|
+
activeAssetCtx(args, listener) {
|
|
1465
|
+
const channel = args.coin.startsWith("@") ? "activeSpotAssetCtx" : "activeAssetCtx";
|
|
1466
|
+
const payload = {
|
|
1467
|
+
type: "activeAssetCtx",
|
|
1468
|
+
coin: args.coin,
|
|
1469
|
+
};
|
|
1470
|
+
return this.transport.subscribe(channel, payload, (e) => {
|
|
1471
|
+
if (e.detail.coin === args.coin) {
|
|
1472
|
+
listener(e.detail);
|
|
1473
|
+
}
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Subscribe to trading data updates for a specific asset and user.
|
|
1478
|
+
* @param args - The parameters for the subscription.
|
|
1479
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1480
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1481
|
+
*
|
|
1482
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1483
|
+
* @example
|
|
1484
|
+
* ```ts
|
|
1485
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1486
|
+
*
|
|
1487
|
+
* const transport = new hl.WebSocketTransport();
|
|
1488
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1489
|
+
*
|
|
1490
|
+
* const sub = await subsClient.activeAssetData({ coin: "BTC", user: "0x..." }, (data) => {
|
|
1491
|
+
* console.log(data);
|
|
1492
|
+
* });
|
|
1493
|
+
* ```
|
|
1494
|
+
*/
|
|
1495
|
+
activeAssetData(args, listener) {
|
|
1496
|
+
const payload = {
|
|
1497
|
+
type: "activeAssetData",
|
|
1498
|
+
coin: args.coin,
|
|
1499
|
+
user: args.user,
|
|
1500
|
+
};
|
|
1501
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1502
|
+
if (e.detail.coin === args.coin && e.detail.user === args.user.toLowerCase()) {
|
|
1503
|
+
listener(e.detail);
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
allMids(args_or_listener, maybeListener) {
|
|
1508
|
+
const args = typeof args_or_listener === "function" ? {} : args_or_listener;
|
|
1509
|
+
const listener = typeof args_or_listener === "function" ? args_or_listener : maybeListener;
|
|
1510
|
+
const payload = {
|
|
1511
|
+
type: "allMids",
|
|
1512
|
+
dex: args.dex,
|
|
1513
|
+
};
|
|
1514
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1515
|
+
listener(e.detail);
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
/**
|
|
1519
|
+
* Subscribe to best bid and offer updates for a specific asset.
|
|
1520
|
+
* @param args - The parameters for the subscription.
|
|
1521
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1522
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1523
|
+
*
|
|
1524
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1525
|
+
* @example
|
|
1526
|
+
* ```ts
|
|
1527
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1528
|
+
*
|
|
1529
|
+
* const transport = new hl.WebSocketTransport();
|
|
1530
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1531
|
+
*
|
|
1532
|
+
* const sub = await subsClient.bbo({ coin: "BTC" }, (data) => {
|
|
1533
|
+
* console.log(data);
|
|
1534
|
+
* });
|
|
1535
|
+
* ```
|
|
1536
|
+
*/
|
|
1537
|
+
bbo(args, listener) {
|
|
1538
|
+
const payload = {
|
|
1539
|
+
type: "bbo",
|
|
1540
|
+
coin: args.coin,
|
|
1541
|
+
};
|
|
1542
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1543
|
+
if (e.detail.coin === args.coin) {
|
|
1544
|
+
listener(e.detail);
|
|
1545
|
+
}
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* Subscribe to candlestick data updates for a specific asset.
|
|
1550
|
+
* @param args - The parameters for the subscription.
|
|
1551
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1552
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1553
|
+
*
|
|
1554
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1555
|
+
* @example
|
|
1556
|
+
* ```ts
|
|
1557
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1558
|
+
*
|
|
1559
|
+
* const transport = new hl.WebSocketTransport();
|
|
1560
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1561
|
+
*
|
|
1562
|
+
* const sub = await subsClient.candle({ coin: "BTC", interval: "1h" }, (data) => {
|
|
1563
|
+
* console.log(data);
|
|
1564
|
+
* });
|
|
1565
|
+
* ```
|
|
1566
|
+
*/
|
|
1567
|
+
candle(args, listener) {
|
|
1568
|
+
const payload = {
|
|
1569
|
+
type: "candle",
|
|
1570
|
+
coin: args.coin,
|
|
1571
|
+
interval: args.interval,
|
|
1572
|
+
};
|
|
1573
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1574
|
+
if (e.detail.s === args.coin && e.detail.i === args.interval) {
|
|
1575
|
+
listener(e.detail);
|
|
1576
|
+
}
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
/**
|
|
1580
|
+
* Subscribe to explorer block updates.
|
|
1581
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1582
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1583
|
+
* @note Make sure the endpoint in the {@link transport} supports this method.
|
|
1584
|
+
*
|
|
1585
|
+
* @see null - no documentation
|
|
1586
|
+
* @example
|
|
1587
|
+
* ```ts
|
|
1588
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1589
|
+
*
|
|
1590
|
+
* const transport = new hl.WebSocketTransport();
|
|
1591
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1592
|
+
*
|
|
1593
|
+
* const sub = await subsClient.explorerBlock((data) => {
|
|
1594
|
+
* console.log(data);
|
|
1595
|
+
* });
|
|
1596
|
+
* ```
|
|
1597
|
+
*/
|
|
1598
|
+
explorerBlock(listener) {
|
|
1599
|
+
const payload = {
|
|
1600
|
+
type: "explorerBlock",
|
|
1601
|
+
};
|
|
1602
|
+
return this.transport.subscribe("_explorerBlock", payload, (e) => {
|
|
1603
|
+
listener(e.detail);
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Subscribe to explorer transaction updates.
|
|
1608
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1609
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1610
|
+
* @note Make sure the endpoint in the {@link transport} supports this method.
|
|
1611
|
+
*
|
|
1612
|
+
* @see null - no documentation
|
|
1613
|
+
* @example
|
|
1614
|
+
* ```ts
|
|
1615
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1616
|
+
*
|
|
1617
|
+
* const transport = new hl.WebSocketTransport();
|
|
1618
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1619
|
+
*
|
|
1620
|
+
* const sub = await subsClient.explorerTxs((data) => {
|
|
1621
|
+
* console.log(data);
|
|
1622
|
+
* });
|
|
1623
|
+
* ```
|
|
1624
|
+
*/
|
|
1625
|
+
explorerTxs(listener) {
|
|
1626
|
+
const payload = {
|
|
1627
|
+
type: "explorerTxs",
|
|
1628
|
+
};
|
|
1629
|
+
return this.transport.subscribe("_explorerTxs", payload, (e) => {
|
|
1630
|
+
listener(e.detail);
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Subscribe to L2 order book updates for a specific asset.
|
|
1635
|
+
* @param args - The parameters for the subscription.
|
|
1636
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1637
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1638
|
+
*
|
|
1639
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1640
|
+
* @example
|
|
1641
|
+
* ```ts
|
|
1642
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1643
|
+
*
|
|
1644
|
+
* const transport = new hl.WebSocketTransport();
|
|
1645
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1646
|
+
*
|
|
1647
|
+
* const sub = await subsClient.l2Book({ coin: "BTC" }, (data) => {
|
|
1648
|
+
* console.log(data);
|
|
1649
|
+
* });
|
|
1650
|
+
* ```
|
|
1651
|
+
*/
|
|
1652
|
+
l2Book(args, listener) {
|
|
1653
|
+
const payload = {
|
|
1654
|
+
type: "l2Book",
|
|
1655
|
+
coin: args.coin,
|
|
1656
|
+
nSigFigs: args.nSigFigs ?? null,
|
|
1657
|
+
mantissa: args.mantissa ?? null,
|
|
1658
|
+
};
|
|
1659
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1660
|
+
if (e.detail.coin === args.coin) {
|
|
1661
|
+
listener(e.detail);
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Subscribe to notification updates for a specific user.
|
|
1667
|
+
* @param args - The parameters for the subscription.
|
|
1668
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1669
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1670
|
+
*
|
|
1671
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1672
|
+
* @example
|
|
1673
|
+
* ```ts
|
|
1674
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1675
|
+
*
|
|
1676
|
+
* const transport = new hl.WebSocketTransport();
|
|
1677
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1678
|
+
*
|
|
1679
|
+
* const sub = await subsClient.notification({ user: "0x..." }, (data) => {
|
|
1680
|
+
* console.log(data);
|
|
1681
|
+
* });
|
|
1682
|
+
* ```
|
|
1683
|
+
*/
|
|
1684
|
+
notification(args, listener) {
|
|
1685
|
+
const payload = {
|
|
1686
|
+
type: "notification",
|
|
1687
|
+
user: args.user,
|
|
1688
|
+
};
|
|
1689
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1690
|
+
listener(e.detail);
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* Subscribe to order status updates for a specific user.
|
|
1695
|
+
* @param args - The parameters for the subscription.
|
|
1696
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1697
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1698
|
+
*
|
|
1699
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1700
|
+
* @example
|
|
1701
|
+
* ```ts
|
|
1702
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1703
|
+
*
|
|
1704
|
+
* const transport = new hl.WebSocketTransport();
|
|
1705
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1706
|
+
*
|
|
1707
|
+
* const sub = await subsClient.orderUpdates({ user: "0x..." }, (data) => {
|
|
1708
|
+
* console.log(data);
|
|
1709
|
+
* });
|
|
1710
|
+
* ```
|
|
1711
|
+
*/
|
|
1712
|
+
orderUpdates(args, listener) {
|
|
1713
|
+
const payload = {
|
|
1714
|
+
type: "orderUpdates",
|
|
1715
|
+
user: args.user,
|
|
1716
|
+
};
|
|
1717
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1718
|
+
listener(e.detail);
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
/**
|
|
1722
|
+
* Subscribe to real-time trade updates for a specific asset.
|
|
1723
|
+
* @param args - The parameters for the subscription.
|
|
1724
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1725
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1726
|
+
*
|
|
1727
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1728
|
+
* @example
|
|
1729
|
+
* ```ts
|
|
1730
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1731
|
+
*
|
|
1732
|
+
* const transport = new hl.WebSocketTransport();
|
|
1733
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1734
|
+
*
|
|
1735
|
+
* const sub = await subsClient.trades({ coin: "BTC" }, (data) => {
|
|
1736
|
+
* console.log(data);
|
|
1737
|
+
* });
|
|
1738
|
+
* ```
|
|
1739
|
+
*/
|
|
1740
|
+
trades(args, listener) {
|
|
1741
|
+
const payload = {
|
|
1742
|
+
type: "trades",
|
|
1743
|
+
coin: args.coin,
|
|
1744
|
+
};
|
|
1745
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1746
|
+
if (e.detail[0]?.coin === args.coin) {
|
|
1747
|
+
listener(e.detail);
|
|
1748
|
+
}
|
|
1749
|
+
});
|
|
1750
|
+
}
|
|
1751
|
+
/**
|
|
1752
|
+
* Subscribe to non-order events for a specific user.
|
|
1753
|
+
* @param args - The parameters for the subscription.
|
|
1754
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1755
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1756
|
+
*
|
|
1757
|
+
* @note Different subscriptions cannot be distinguished from each other.
|
|
1758
|
+
*
|
|
1759
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1760
|
+
* @example
|
|
1761
|
+
* ```ts
|
|
1762
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1763
|
+
*
|
|
1764
|
+
* const transport = new hl.WebSocketTransport();
|
|
1765
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1766
|
+
*
|
|
1767
|
+
* const sub = await subsClient.userEvents({ user: "0x..." }, (data) => {
|
|
1768
|
+
* console.log(data);
|
|
1769
|
+
* });
|
|
1770
|
+
* ```
|
|
1771
|
+
*/
|
|
1772
|
+
userEvents(args, listener) {
|
|
1773
|
+
const payload = {
|
|
1774
|
+
type: "userEvents",
|
|
1775
|
+
user: args.user,
|
|
1776
|
+
};
|
|
1777
|
+
return this.transport.subscribe("user", payload, (e) => {
|
|
1778
|
+
listener(e.detail);
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Subscribe to trade fill updates for a specific user.
|
|
1783
|
+
* @param args - The parameters for the subscription.
|
|
1784
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1785
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1786
|
+
*
|
|
1787
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1788
|
+
* @example
|
|
1789
|
+
* ```ts
|
|
1790
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1791
|
+
*
|
|
1792
|
+
* const transport = new hl.WebSocketTransport();
|
|
1793
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1794
|
+
*
|
|
1795
|
+
* const sub = await subsClient.userFills({ user: "0x..." }, (data) => {
|
|
1796
|
+
* console.log(data);
|
|
1797
|
+
* });
|
|
1798
|
+
* ```
|
|
1799
|
+
*/
|
|
1800
|
+
userFills(args, listener) {
|
|
1801
|
+
const payload = {
|
|
1802
|
+
type: "userFills",
|
|
1803
|
+
user: args.user,
|
|
1804
|
+
aggregateByTime: args.aggregateByTime ?? false,
|
|
1805
|
+
};
|
|
1806
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1807
|
+
if (e.detail.user === args.user.toLowerCase()) {
|
|
1808
|
+
listener(e.detail);
|
|
1809
|
+
}
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Subscribe to funding payment updates for a specific user.
|
|
1814
|
+
* @param args - The parameters for the subscription.
|
|
1815
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1816
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1817
|
+
*
|
|
1818
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1819
|
+
* @example
|
|
1820
|
+
* ```ts
|
|
1821
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1822
|
+
*
|
|
1823
|
+
* const transport = new hl.WebSocketTransport();
|
|
1824
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1825
|
+
*
|
|
1826
|
+
* const sub = await subsClient.userFundings({ user: "0x..." }, (data) => {
|
|
1827
|
+
* console.log(data);
|
|
1828
|
+
* });
|
|
1829
|
+
* ```
|
|
1830
|
+
*/
|
|
1831
|
+
userFundings(args, listener) {
|
|
1832
|
+
const payload = {
|
|
1833
|
+
type: "userFundings",
|
|
1834
|
+
user: args.user,
|
|
1835
|
+
};
|
|
1836
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1837
|
+
if (e.detail.user === args.user.toLowerCase()) {
|
|
1838
|
+
listener(e.detail);
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1843
|
+
* Subscribe to non-funding ledger updates for a specific user.
|
|
1844
|
+
* @param args - The parameters for the subscription.
|
|
1845
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1846
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1847
|
+
*
|
|
1848
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1849
|
+
* @example
|
|
1850
|
+
* ```ts
|
|
1851
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1852
|
+
*
|
|
1853
|
+
* const transport = new hl.WebSocketTransport();
|
|
1854
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1855
|
+
*
|
|
1856
|
+
* const sub = await subsClient.userNonFundingLedgerUpdates({ user: "0x..." }, (data) => {
|
|
1857
|
+
* console.log(data);
|
|
1858
|
+
* });
|
|
1859
|
+
* ```
|
|
1860
|
+
*/
|
|
1861
|
+
userNonFundingLedgerUpdates(args, listener) {
|
|
1862
|
+
const payload = {
|
|
1863
|
+
type: "userNonFundingLedgerUpdates",
|
|
1864
|
+
user: args.user,
|
|
1865
|
+
};
|
|
1866
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1867
|
+
if (e.detail.user === args.user.toLowerCase()) {
|
|
1868
|
+
listener(e.detail);
|
|
1869
|
+
}
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* Subscribe to TWAP order history updates for a specific user.
|
|
1874
|
+
* @param args - The parameters for the subscription.
|
|
1875
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1876
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1877
|
+
*
|
|
1878
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1879
|
+
* @example
|
|
1880
|
+
* ```ts
|
|
1881
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1882
|
+
*
|
|
1883
|
+
* const transport = new hl.WebSocketTransport();
|
|
1884
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1885
|
+
*
|
|
1886
|
+
* const sub = await subsClient.userTwapHistory({ user: "0x..." }, (data) => {
|
|
1887
|
+
* console.log(data);
|
|
1888
|
+
* });
|
|
1889
|
+
* ```
|
|
1890
|
+
*/
|
|
1891
|
+
userTwapHistory(args, listener) {
|
|
1892
|
+
const payload = {
|
|
1893
|
+
type: "userTwapHistory",
|
|
1894
|
+
user: args.user,
|
|
1895
|
+
};
|
|
1896
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1897
|
+
if (e.detail.user === args.user.toLowerCase()) {
|
|
1898
|
+
listener(e.detail);
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Subscribe to TWAP execution updates for a specific user.
|
|
1904
|
+
* @param args - The parameters for the subscription.
|
|
1905
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1906
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1907
|
+
*
|
|
1908
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1909
|
+
* @example
|
|
1910
|
+
* ```ts
|
|
1911
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1912
|
+
*
|
|
1913
|
+
* const transport = new hl.WebSocketTransport();
|
|
1914
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1915
|
+
*
|
|
1916
|
+
* const sub = await subsClient.userTwapSliceFills({ user: "0x..." }, (data) => {
|
|
1917
|
+
* console.log(data);
|
|
1918
|
+
* });
|
|
1919
|
+
* ```
|
|
1920
|
+
*/
|
|
1921
|
+
userTwapSliceFills(args, listener) {
|
|
1922
|
+
const payload = {
|
|
1923
|
+
type: "userTwapSliceFills",
|
|
1924
|
+
user: args.user,
|
|
1925
|
+
};
|
|
1926
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1927
|
+
if (e.detail.user === args.user.toLowerCase()) {
|
|
1928
|
+
listener(e.detail);
|
|
1929
|
+
}
|
|
1930
|
+
});
|
|
1931
|
+
}
|
|
1932
|
+
/**
|
|
1933
|
+
* Subscribe to comprehensive user and market data updates.
|
|
1934
|
+
* @param args - The parameters for the subscription.
|
|
1935
|
+
* @param listener - The callback function to be called when the event is received.
|
|
1936
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
1937
|
+
*
|
|
1938
|
+
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions
|
|
1939
|
+
* @example
|
|
1940
|
+
* ```ts
|
|
1941
|
+
* import * as hl from "@nktkas/hyperliquid";
|
|
1942
|
+
*
|
|
1943
|
+
* const transport = new hl.WebSocketTransport();
|
|
1944
|
+
* const subsClient = new hl.SubscriptionClient({ transport });
|
|
1945
|
+
*
|
|
1946
|
+
* const sub = await subsClient.webData2({ user: "0x..." }, (data) => {
|
|
1947
|
+
* console.log(data);
|
|
1948
|
+
* });
|
|
1949
|
+
* ```
|
|
1950
|
+
*/
|
|
1951
|
+
webData2(args, listener) {
|
|
1952
|
+
const payload = {
|
|
1953
|
+
type: "webData2",
|
|
1954
|
+
user: args.user,
|
|
1955
|
+
};
|
|
1956
|
+
return this.transport.subscribe(payload.type, payload, (e) => {
|
|
1957
|
+
if (e.detail.user === args.user.toLowerCase()) {
|
|
1958
|
+
listener(e.detail);
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
}
|
|
1962
|
+
async [Symbol.asyncDispose]() {
|
|
1963
|
+
await this.transport[Symbol.asyncDispose]?.();
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
/**
|
|
1968
|
+
* Error thrown when an HTTP response is deemed invalid:
|
|
1969
|
+
* - Non-200 status code
|
|
1970
|
+
* - Unexpected content type
|
|
1971
|
+
*/
|
|
1972
|
+
class HttpRequestError extends TransportError {
|
|
1973
|
+
response;
|
|
1974
|
+
responseBody;
|
|
1975
|
+
/**
|
|
1976
|
+
* Creates a new HTTP request error.
|
|
1977
|
+
* @param response - The failed HTTP response.
|
|
1978
|
+
* @param responseBody - The raw response body content, if available.
|
|
1979
|
+
*/
|
|
1980
|
+
constructor(response, responseBody) {
|
|
1981
|
+
let message = `HTTP request failed: status ${response.status}`;
|
|
1982
|
+
if (responseBody)
|
|
1983
|
+
message += `, body "${responseBody}"`;
|
|
1984
|
+
super(message);
|
|
1985
|
+
this.response = response;
|
|
1986
|
+
this.responseBody = responseBody;
|
|
1987
|
+
this.name = "HttpRequestError";
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
/** HTTP implementation of the REST transport interface. */
|
|
1991
|
+
class HttpTransport {
|
|
1992
|
+
isTestnet;
|
|
1993
|
+
timeout;
|
|
1994
|
+
server;
|
|
1995
|
+
fetchOptions;
|
|
1996
|
+
onRequest;
|
|
1997
|
+
onResponse;
|
|
1998
|
+
/**
|
|
1999
|
+
* Creates a new HTTP transport instance.
|
|
2000
|
+
* @param options - Configuration options for the HTTP transport layer.
|
|
2001
|
+
*/
|
|
2002
|
+
constructor(options) {
|
|
2003
|
+
this.isTestnet = options?.isTestnet ?? false;
|
|
2004
|
+
this.timeout = options?.timeout === undefined ? 10_000 : options.timeout;
|
|
2005
|
+
this.server = {
|
|
2006
|
+
mainnet: {
|
|
2007
|
+
api: options?.server?.mainnet?.api ?? "https://api.hyperliquid.xyz",
|
|
2008
|
+
rpc: options?.server?.mainnet?.rpc ?? "https://rpc.hyperliquid.xyz",
|
|
2009
|
+
},
|
|
2010
|
+
testnet: {
|
|
2011
|
+
api: options?.server?.testnet?.api ?? "https://api.hyperliquid-testnet.xyz",
|
|
2012
|
+
rpc: options?.server?.testnet?.rpc ?? "https://rpc.hyperliquid-testnet.xyz",
|
|
2013
|
+
},
|
|
2014
|
+
};
|
|
2015
|
+
this.fetchOptions = options?.fetchOptions ?? {};
|
|
2016
|
+
this.onRequest = options?.onRequest;
|
|
2017
|
+
this.onResponse = options?.onResponse;
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* Sends a request to the Hyperliquid API via fetch.
|
|
2021
|
+
* @param endpoint - The API endpoint to send the request to.
|
|
2022
|
+
* @param payload - The payload to send with the request.
|
|
2023
|
+
* @param signal - An optional abort signal.
|
|
2024
|
+
* @returns A promise that resolves with parsed JSON response body.
|
|
2025
|
+
* @throws {HttpRequestError} - Thrown when an HTTP response is deemed invalid.
|
|
2026
|
+
* @throws May throw {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch#exceptions | fetch errors}.
|
|
2027
|
+
*/
|
|
2028
|
+
async request(endpoint, payload, signal) {
|
|
2029
|
+
// Construct a Request
|
|
2030
|
+
const url = new URL(endpoint, this.server[this.isTestnet ? "testnet" : "mainnet"][endpoint === "explorer" ? "rpc" : "api"]);
|
|
2031
|
+
const init = mergeRequestInit({
|
|
2032
|
+
body: JSON.stringify(payload),
|
|
2033
|
+
headers: {
|
|
2034
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
2035
|
+
"Content-Type": "application/json",
|
|
2036
|
+
},
|
|
2037
|
+
keepalive: true,
|
|
2038
|
+
method: "POST",
|
|
2039
|
+
signal: this.timeout ? AbortSignal.timeout(this.timeout) : undefined,
|
|
2040
|
+
}, this.fetchOptions, { signal });
|
|
2041
|
+
let request = new Request(url, init);
|
|
2042
|
+
// Call the onRequest callback, if provided
|
|
2043
|
+
if (this.onRequest) {
|
|
2044
|
+
const customRequest = await this.onRequest(request);
|
|
2045
|
+
if (customRequest instanceof Request)
|
|
2046
|
+
request = customRequest;
|
|
2047
|
+
}
|
|
2048
|
+
// Send the Request and wait for a Response
|
|
2049
|
+
let response = await fetch(request);
|
|
2050
|
+
// Call the onResponse callback, if provided
|
|
2051
|
+
if (this.onResponse) {
|
|
2052
|
+
const customResponse = await this.onResponse(response);
|
|
2053
|
+
if (customResponse instanceof Response)
|
|
2054
|
+
response = customResponse;
|
|
2055
|
+
}
|
|
2056
|
+
// Validate the Response
|
|
2057
|
+
if (!response.ok || !response.headers.get("Content-Type")?.includes("application/json")) {
|
|
2058
|
+
// Unload the response body to prevent memory leaks
|
|
2059
|
+
const body = await response.text().catch(() => undefined);
|
|
2060
|
+
throw new HttpRequestError(response, body);
|
|
2061
|
+
}
|
|
2062
|
+
// Parse the response body
|
|
2063
|
+
const body = await response.json();
|
|
2064
|
+
// Check if the response is an error
|
|
2065
|
+
if (body?.type === "error") {
|
|
2066
|
+
throw new HttpRequestError(response, body?.message);
|
|
2067
|
+
}
|
|
2068
|
+
// Return the response body
|
|
2069
|
+
return body;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
/** Merges multiple {@linkcode HeadersInit} into one {@linkcode Headers}. */
|
|
2073
|
+
function mergeHeadersInit(...inits) {
|
|
2074
|
+
if (inits.length === 0 || inits.length === 1) {
|
|
2075
|
+
return new Headers(inits[0]);
|
|
2076
|
+
}
|
|
2077
|
+
const merged = new Headers();
|
|
2078
|
+
for (const headers of inits) {
|
|
2079
|
+
const iterator = Symbol.iterator in headers ? headers : Object.entries(headers);
|
|
2080
|
+
for (const [key, value] of iterator) {
|
|
2081
|
+
merged.set(key, value);
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
return merged;
|
|
2085
|
+
}
|
|
2086
|
+
/** Merges multiple {@linkcode RequestInit} into one {@linkcode RequestInit}. */
|
|
2087
|
+
function mergeRequestInit(...inits) {
|
|
2088
|
+
const merged = inits.reduce((acc, init) => ({ ...acc, ...init }), {});
|
|
2089
|
+
const headersList = inits.map((init) => init.headers)
|
|
2090
|
+
.filter((headers) => typeof headers === "object");
|
|
2091
|
+
if (headersList.length > 0) {
|
|
2092
|
+
merged.headers = mergeHeadersInit(...headersList);
|
|
2093
|
+
}
|
|
2094
|
+
const signals = inits.map((init) => init.signal)
|
|
2095
|
+
.filter((signal) => signal instanceof AbortSignal);
|
|
2096
|
+
if (signals.length > 0) {
|
|
2097
|
+
merged.signal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];
|
|
2098
|
+
}
|
|
2099
|
+
return merged;
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
// Copyright 2018-2025 the Deno authors. MIT license.
|
|
2103
|
+
// This module is browser compatible.
|
|
2104
|
+
/**
|
|
2105
|
+
* Resolve a {@linkcode Promise} after a given amount of milliseconds.
|
|
2106
|
+
*
|
|
2107
|
+
* @throws {DOMException} If the optional signal is aborted before the delay
|
|
2108
|
+
* duration, and `signal.reason` is undefined.
|
|
2109
|
+
* @param ms Duration in milliseconds for how long the delay should last.
|
|
2110
|
+
* @param options Additional options.
|
|
2111
|
+
*
|
|
2112
|
+
* @example Basic usage
|
|
2113
|
+
* ```ts no-assert
|
|
2114
|
+
* import { delay } from "@std/async/delay";
|
|
2115
|
+
*
|
|
2116
|
+
* // ...
|
|
2117
|
+
* const delayedPromise = delay(100);
|
|
2118
|
+
* const result = await delayedPromise;
|
|
2119
|
+
* // ...
|
|
2120
|
+
* ```
|
|
2121
|
+
*
|
|
2122
|
+
* @example Disable persistence
|
|
2123
|
+
*
|
|
2124
|
+
* Setting `persistent` to `false` will allow the process to continue to run as
|
|
2125
|
+
* long as the timer exists.
|
|
2126
|
+
*
|
|
2127
|
+
* ```ts no-assert ignore
|
|
2128
|
+
* import { delay } from "@std/async/delay";
|
|
2129
|
+
*
|
|
2130
|
+
* // ...
|
|
2131
|
+
* await delay(100, { persistent: false });
|
|
2132
|
+
* // ...
|
|
2133
|
+
* ```
|
|
2134
|
+
*/
|
|
2135
|
+
function delay(ms, options = {}) {
|
|
2136
|
+
const { signal, persistent = true } = options;
|
|
2137
|
+
if (signal?.aborted)
|
|
2138
|
+
return Promise.reject(signal.reason);
|
|
2139
|
+
return new Promise((resolve, reject) => {
|
|
2140
|
+
const abort = () => {
|
|
2141
|
+
clearTimeout(i);
|
|
2142
|
+
reject(signal?.reason);
|
|
2143
|
+
};
|
|
2144
|
+
const done = () => {
|
|
2145
|
+
signal?.removeEventListener("abort", abort);
|
|
2146
|
+
resolve();
|
|
2147
|
+
};
|
|
2148
|
+
const i = setTimeout(done, ms);
|
|
2149
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
2150
|
+
if (persistent === false) {
|
|
2151
|
+
try {
|
|
2152
|
+
// @ts-ignore For browser compatibility
|
|
2153
|
+
Deno.unrefTimer(i);
|
|
2154
|
+
}
|
|
2155
|
+
catch (error) {
|
|
2156
|
+
if (!(error instanceof ReferenceError)) {
|
|
2157
|
+
throw error;
|
|
2158
|
+
}
|
|
2159
|
+
// deno-lint-ignore no-console
|
|
2160
|
+
console.error("`persistent` option is only available in Deno");
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
// deno-lint-ignore-file no-explicit-any
|
|
2167
|
+
/** Simple FIFO (First In, First Out) buffer implementation. */
|
|
2168
|
+
class FIFOMessageBuffer {
|
|
2169
|
+
queue = [];
|
|
2170
|
+
push(data, signal) {
|
|
2171
|
+
this.queue.push({ data, signal });
|
|
2172
|
+
}
|
|
2173
|
+
*[Symbol.iterator]() {
|
|
2174
|
+
while (this.queue.length > 0) {
|
|
2175
|
+
const { data, signal } = this.queue.shift();
|
|
2176
|
+
if (signal?.aborted)
|
|
2177
|
+
continue;
|
|
2178
|
+
yield data;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
/** Error thrown when reconnection problems occur. */
|
|
2183
|
+
class ReconnectingWebSocketError extends TransportError {
|
|
2184
|
+
code;
|
|
2185
|
+
constructor(code, cause) {
|
|
2186
|
+
super(`Error when reconnecting WebSocket: ${code}`);
|
|
2187
|
+
this.code = code;
|
|
2188
|
+
this.name = "ReconnectingWebSocketError";
|
|
2189
|
+
this.cause = cause;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
/**
|
|
2193
|
+
* A WebSocket that automatically reconnects when disconnected.
|
|
2194
|
+
* Fully compatible with standard WebSocket API.
|
|
2195
|
+
*/
|
|
2196
|
+
class ReconnectingWebSocket {
|
|
2197
|
+
_socket;
|
|
2198
|
+
_protocols;
|
|
2199
|
+
_listeners = [];
|
|
2200
|
+
_attempt = 0;
|
|
2201
|
+
reconnectOptions;
|
|
2202
|
+
reconnectAbortController = new AbortController();
|
|
2203
|
+
constructor(url, protocols, options) {
|
|
2204
|
+
this.reconnectOptions = {
|
|
2205
|
+
maxRetries: options?.maxRetries ?? 3,
|
|
2206
|
+
connectionTimeout: options?.connectionTimeout === undefined ? 10_000 : options.connectionTimeout,
|
|
2207
|
+
connectionDelay: options?.connectionDelay ?? ((n) => Math.min(~~(1 << n) * 150, 10_000)),
|
|
2208
|
+
shouldReconnect: options?.shouldReconnect ?? (() => true),
|
|
2209
|
+
messageBuffer: options?.messageBuffer ?? new FIFOMessageBuffer(),
|
|
2210
|
+
};
|
|
2211
|
+
this._socket = this._createSocket(url, protocols);
|
|
2212
|
+
this._protocols = protocols;
|
|
2213
|
+
this._setupEventListeners();
|
|
2214
|
+
}
|
|
2215
|
+
_createSocket(url, protocols) {
|
|
2216
|
+
const socket = new WebSocket(url, protocols);
|
|
2217
|
+
if (this.reconnectOptions.connectionTimeout === null)
|
|
2218
|
+
return socket;
|
|
2219
|
+
const timeoutId = setTimeout(() => {
|
|
2220
|
+
socket.removeEventListener("open", openHandler);
|
|
2221
|
+
socket.removeEventListener("close", closeHandler);
|
|
2222
|
+
socket.close(3008, "Timeout"); // https://www.iana.org/assignments/websocket/websocket.xml#close-code-number
|
|
2223
|
+
}, this.reconnectOptions.connectionTimeout);
|
|
2224
|
+
const openHandler = () => {
|
|
2225
|
+
socket.removeEventListener("close", closeHandler);
|
|
2226
|
+
clearTimeout(timeoutId);
|
|
2227
|
+
};
|
|
2228
|
+
const closeHandler = () => {
|
|
2229
|
+
socket.removeEventListener("open", openHandler);
|
|
2230
|
+
clearTimeout(timeoutId);
|
|
2231
|
+
};
|
|
2232
|
+
socket.addEventListener("open", openHandler, { once: true });
|
|
2233
|
+
socket.addEventListener("close", closeHandler, { once: true });
|
|
2234
|
+
return socket;
|
|
2235
|
+
}
|
|
2236
|
+
/** Initializes the internal event listeners for the socket. */
|
|
2237
|
+
_setupEventListeners() {
|
|
2238
|
+
this._socket.addEventListener("open", this._open, { once: true });
|
|
2239
|
+
this._socket.addEventListener("close", this._close, { once: true });
|
|
2240
|
+
}
|
|
2241
|
+
_open = () => {
|
|
2242
|
+
// Reset the attempt counter
|
|
2243
|
+
this._attempt = 0;
|
|
2244
|
+
// Send all buffered messages
|
|
2245
|
+
for (const message of this.reconnectOptions.messageBuffer) {
|
|
2246
|
+
this._socket.send(message);
|
|
2247
|
+
}
|
|
2248
|
+
};
|
|
2249
|
+
_close = async (event) => {
|
|
2250
|
+
try {
|
|
2251
|
+
// If the event was triggered but the socket is not closing, ignore it
|
|
2252
|
+
if (this._socket.readyState !== ReconnectingWebSocket.CLOSING &&
|
|
2253
|
+
this._socket.readyState !== ReconnectingWebSocket.CLOSED)
|
|
2254
|
+
return;
|
|
2255
|
+
// If the instance is terminated, do not attempt to reconnect
|
|
2256
|
+
if (this.reconnectAbortController.signal.aborted)
|
|
2257
|
+
return;
|
|
2258
|
+
// Check if reconnection should be attempted
|
|
2259
|
+
if (++this._attempt > this.reconnectOptions.maxRetries) {
|
|
2260
|
+
this._cleanup("RECONNECTION_LIMIT_REACHED");
|
|
2261
|
+
return;
|
|
2262
|
+
}
|
|
2263
|
+
const userDecision = await this.reconnectOptions.shouldReconnect(event, this.reconnectAbortController.signal);
|
|
2264
|
+
if (this.reconnectAbortController.signal.aborted)
|
|
2265
|
+
return;
|
|
2266
|
+
if (!userDecision) {
|
|
2267
|
+
this._cleanup("RECONNECTION_STOPPED_BY_USER");
|
|
2268
|
+
return;
|
|
2269
|
+
}
|
|
2270
|
+
// Delay before reconnecting
|
|
2271
|
+
const reconnectDelay = typeof this.reconnectOptions.connectionDelay === "number"
|
|
2272
|
+
? this.reconnectOptions.connectionDelay
|
|
2273
|
+
: await this.reconnectOptions.connectionDelay(this._attempt, this.reconnectAbortController.signal);
|
|
2274
|
+
if (this.reconnectAbortController.signal.aborted)
|
|
2275
|
+
return;
|
|
2276
|
+
await delay(reconnectDelay, { signal: this.reconnectAbortController.signal });
|
|
2277
|
+
// Create a new WebSocket instance
|
|
2278
|
+
const { onclose, onerror, onmessage, onopen } = this._socket;
|
|
2279
|
+
this._socket = this._createSocket(this._socket.url, this._protocols);
|
|
2280
|
+
// Reconnect all listeners
|
|
2281
|
+
this._setupEventListeners();
|
|
2282
|
+
this._listeners.forEach(({ type, listenerProxy, options }) => {
|
|
2283
|
+
this._socket.addEventListener(type, listenerProxy, options);
|
|
2284
|
+
});
|
|
2285
|
+
this._socket.onclose = onclose;
|
|
2286
|
+
this._socket.onerror = onerror;
|
|
2287
|
+
this._socket.onmessage = onmessage;
|
|
2288
|
+
this._socket.onopen = onopen;
|
|
2289
|
+
}
|
|
2290
|
+
catch (error) {
|
|
2291
|
+
this._cleanup("UNKNOWN_ERROR", error);
|
|
2292
|
+
}
|
|
2293
|
+
};
|
|
2294
|
+
/** Clean up internal resources. */
|
|
2295
|
+
_cleanup(code, cause) {
|
|
2296
|
+
this.reconnectAbortController.abort(new ReconnectingWebSocketError(code, cause));
|
|
2297
|
+
this._listeners = [];
|
|
2298
|
+
this._socket.close();
|
|
2299
|
+
}
|
|
2300
|
+
// WebSocket property implementations
|
|
2301
|
+
get url() {
|
|
2302
|
+
return this._socket.url;
|
|
2303
|
+
}
|
|
2304
|
+
get readyState() {
|
|
2305
|
+
return this._socket.readyState;
|
|
2306
|
+
}
|
|
2307
|
+
get bufferedAmount() {
|
|
2308
|
+
return this._socket.bufferedAmount;
|
|
2309
|
+
}
|
|
2310
|
+
get extensions() {
|
|
2311
|
+
return this._socket.extensions;
|
|
2312
|
+
}
|
|
2313
|
+
get protocol() {
|
|
2314
|
+
return this._socket.protocol;
|
|
2315
|
+
}
|
|
2316
|
+
get binaryType() {
|
|
2317
|
+
return this._socket.binaryType;
|
|
2318
|
+
}
|
|
2319
|
+
set binaryType(value) {
|
|
2320
|
+
this._socket.binaryType = value;
|
|
2321
|
+
}
|
|
2322
|
+
CONNECTING = 0;
|
|
2323
|
+
OPEN = 1;
|
|
2324
|
+
CLOSING = 2;
|
|
2325
|
+
CLOSED = 3;
|
|
2326
|
+
static CONNECTING = 0;
|
|
2327
|
+
static OPEN = 1;
|
|
2328
|
+
static CLOSING = 2;
|
|
2329
|
+
static CLOSED = 3;
|
|
2330
|
+
get onclose() {
|
|
2331
|
+
return this._socket.onclose;
|
|
2332
|
+
}
|
|
2333
|
+
set onclose(value) {
|
|
2334
|
+
this._socket.onclose = value;
|
|
2335
|
+
}
|
|
2336
|
+
get onerror() {
|
|
2337
|
+
return this._socket.onerror;
|
|
2338
|
+
}
|
|
2339
|
+
set onerror(value) {
|
|
2340
|
+
this._socket.onerror = value;
|
|
2341
|
+
}
|
|
2342
|
+
get onmessage() {
|
|
2343
|
+
return this._socket.onmessage;
|
|
2344
|
+
}
|
|
2345
|
+
set onmessage(value) {
|
|
2346
|
+
this._socket.onmessage = value;
|
|
2347
|
+
}
|
|
2348
|
+
get onopen() {
|
|
2349
|
+
return this._socket.onopen;
|
|
2350
|
+
}
|
|
2351
|
+
set onopen(value) {
|
|
2352
|
+
this._socket.onopen = value;
|
|
2353
|
+
}
|
|
2354
|
+
/**
|
|
2355
|
+
* @param permanently - If `true`, the connection will be permanently closed. Default is `true`.
|
|
2356
|
+
*/
|
|
2357
|
+
close(code, reason, permanently = true) {
|
|
2358
|
+
this._socket.close(code, reason);
|
|
2359
|
+
if (permanently)
|
|
2360
|
+
this._cleanup("USER_INITIATED_CLOSE");
|
|
165
2361
|
}
|
|
166
2362
|
/**
|
|
167
|
-
*
|
|
2363
|
+
* @param signal - `AbortSignal` to cancel sending a message if it was in the buffer.
|
|
2364
|
+
* @note If the connection is not open, the data will be buffered and sent when the connection is established.
|
|
168
2365
|
*/
|
|
169
|
-
|
|
170
|
-
|
|
2366
|
+
send(data, signal) {
|
|
2367
|
+
if (signal?.aborted)
|
|
2368
|
+
return;
|
|
2369
|
+
if (this._socket.readyState !== ReconnectingWebSocket.OPEN && !this.reconnectAbortController.signal.aborted) {
|
|
2370
|
+
this.reconnectOptions.messageBuffer.push(data, signal);
|
|
2371
|
+
}
|
|
2372
|
+
else {
|
|
2373
|
+
this._socket.send(data);
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
addEventListener(type, listener, options) {
|
|
2377
|
+
// Wrap the listener to handle reconnection
|
|
2378
|
+
let listenerProxy;
|
|
2379
|
+
if (this.reconnectAbortController.signal.aborted) {
|
|
2380
|
+
// If the instance is terminated, use the original listener
|
|
2381
|
+
listenerProxy = listener;
|
|
2382
|
+
}
|
|
2383
|
+
else {
|
|
2384
|
+
// Check if the listener is already registered
|
|
2385
|
+
const index = this._listeners.findIndex((e) => listenersMatch(e, { type, listener, options }));
|
|
2386
|
+
if (index !== -1) {
|
|
2387
|
+
// Use the existing listener proxy
|
|
2388
|
+
listenerProxy = this._listeners[index].listenerProxy;
|
|
2389
|
+
}
|
|
2390
|
+
else {
|
|
2391
|
+
// Wrap the original listener to follow the once option when reconnecting
|
|
2392
|
+
listenerProxy = (event) => {
|
|
2393
|
+
try {
|
|
2394
|
+
if (typeof listener === "function") {
|
|
2395
|
+
listener.call(this, event);
|
|
2396
|
+
}
|
|
2397
|
+
else {
|
|
2398
|
+
listener.handleEvent(event);
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
finally {
|
|
2402
|
+
// If the listener is marked as once, remove it after the first invocation
|
|
2403
|
+
if (typeof options === "object" && options.once === true) {
|
|
2404
|
+
const index = this._listeners.findIndex((e) => listenersMatch(e, { type, listener, options }));
|
|
2405
|
+
if (index !== -1) {
|
|
2406
|
+
this._listeners.splice(index, 1);
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
};
|
|
2411
|
+
this._listeners.push({ type, listener, options, listenerProxy });
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
// Add the wrapped (or original) listener
|
|
2415
|
+
this._socket.addEventListener(type, listenerProxy, options);
|
|
2416
|
+
}
|
|
2417
|
+
removeEventListener(type, listener, options) {
|
|
2418
|
+
// Remove a wrapped listener, not an original listener
|
|
2419
|
+
const index = this._listeners.findIndex((e) => listenersMatch(e, { type, listener, options }));
|
|
2420
|
+
if (index !== -1) {
|
|
2421
|
+
const { listenerProxy } = this._listeners[index];
|
|
2422
|
+
this._socket.removeEventListener(type, listenerProxy, options);
|
|
2423
|
+
this._listeners.splice(index, 1);
|
|
2424
|
+
}
|
|
2425
|
+
else {
|
|
2426
|
+
// If the wrapped listener is not found, remove the original listener
|
|
2427
|
+
this._socket.removeEventListener(type, listener, options);
|
|
2428
|
+
}
|
|
171
2429
|
}
|
|
2430
|
+
dispatchEvent(event) {
|
|
2431
|
+
return this._socket.dispatchEvent(event);
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
/** Check if two event listeners are the same (just like EventTarget). */
|
|
2435
|
+
function listenersMatch(a, b) {
|
|
2436
|
+
// EventTarget only compares capture in options, even if one is an object and the other is boolean
|
|
2437
|
+
const aCapture = Boolean(typeof a.options === "object" ? a.options.capture : a.options);
|
|
2438
|
+
const bCapture = Boolean(typeof b.options === "object" ? b.options.capture : b.options);
|
|
2439
|
+
return a.type === b.type && a.listener === b.listener && aCapture === bCapture;
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
class TypedEventTarget extends EventTarget {
|
|
172
2443
|
/**
|
|
173
|
-
*
|
|
2444
|
+
* Dispatches a synthetic event event to target and returns true if either
|
|
2445
|
+
* event's cancelable attribute value is false or its preventDefault() method
|
|
2446
|
+
* was not invoked, and false otherwise.
|
|
174
2447
|
*/
|
|
175
|
-
|
|
176
|
-
|
|
2448
|
+
dispatchTypedEvent(_type, event) {
|
|
2449
|
+
return super.dispatchEvent(event);
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
/** Listens for WebSocket messages and sends them as Hyperliquid typed events. */
|
|
2454
|
+
class HyperliquidEventTarget extends TypedEventTarget {
|
|
2455
|
+
constructor(socket) {
|
|
2456
|
+
super();
|
|
2457
|
+
socket.addEventListener("message", (event) => {
|
|
2458
|
+
try {
|
|
2459
|
+
const msg = JSON.parse(event.data);
|
|
2460
|
+
if (isHyperliquidMsg(msg)) {
|
|
2461
|
+
this.dispatchEvent(new CustomEvent(msg.channel, { detail: msg.data }));
|
|
2462
|
+
}
|
|
2463
|
+
else if (isExplorerBlockMsg(msg)) {
|
|
2464
|
+
this.dispatchEvent(new CustomEvent("_explorerBlock", { detail: msg }));
|
|
2465
|
+
}
|
|
2466
|
+
else if (isExplorerTxsMsg(msg)) {
|
|
2467
|
+
this.dispatchEvent(new CustomEvent("_explorerTxs", { detail: msg }));
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
catch {
|
|
2471
|
+
// Ignore JSON parsing errors
|
|
2472
|
+
}
|
|
2473
|
+
});
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
/** Type guard for Hyperliquid messages. */
|
|
2477
|
+
function isHyperliquidMsg(value) {
|
|
2478
|
+
return typeof value === "object" && value !== null &&
|
|
2479
|
+
"channel" in value && typeof value.channel === "string";
|
|
2480
|
+
}
|
|
2481
|
+
/** Type guard for explorer block messages. */
|
|
2482
|
+
function isExplorerBlockMsg(value) {
|
|
2483
|
+
return Array.isArray(value) && value.length > 0 &&
|
|
2484
|
+
(typeof value[0] === "object" && value[0] !== null && !Array.isArray(value[0]) &&
|
|
2485
|
+
"height" in value[0] && typeof value[0].height === "number" &&
|
|
2486
|
+
"blockTime" in value[0] && typeof value[0].blockTime === "number" &&
|
|
2487
|
+
"hash" in value[0] && typeof value[0].hash === "string" &&
|
|
2488
|
+
"proposer" in value[0] && typeof value[0].proposer === "string" &&
|
|
2489
|
+
"numTxs" in value[0] && typeof value[0].numTxs === "number");
|
|
2490
|
+
}
|
|
2491
|
+
/** Type guard for explorer transactions messages. */
|
|
2492
|
+
function isExplorerTxsMsg(value) {
|
|
2493
|
+
return Array.isArray(value) && value.length > 0 &&
|
|
2494
|
+
(typeof value[0] === "object" && value[0] !== null && !Array.isArray(value[0]) &&
|
|
2495
|
+
"action" in value[0] && typeof value[0].action === "object" && value[0].action !== null &&
|
|
2496
|
+
"block" in value[0] && typeof value[0].block === "number" &&
|
|
2497
|
+
"error" in value[0] && (typeof value[0].error === "string" || value[0].error === null) &&
|
|
2498
|
+
"hash" in value[0] && typeof value[0].hash === "string" &&
|
|
2499
|
+
"time" in value[0] && typeof value[0].time === "number" &&
|
|
2500
|
+
"user" in value[0] && typeof value[0].user === "string");
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
/**
|
|
2504
|
+
* Error thrown when a WebSocket request fails:
|
|
2505
|
+
* - When the WebSocket connection is closed
|
|
2506
|
+
* - When the server responds with an error message
|
|
2507
|
+
*/
|
|
2508
|
+
class WebSocketRequestError extends TransportError {
|
|
2509
|
+
constructor(message) {
|
|
2510
|
+
super(message);
|
|
2511
|
+
this.name = "WebSocketRequestError";
|
|
177
2512
|
}
|
|
2513
|
+
}
|
|
2514
|
+
/**
|
|
2515
|
+
* Manages WebSocket requests to the Hyperliquid API.
|
|
2516
|
+
* Handles request creation, sending, and mapping responses to their corresponding requests.
|
|
2517
|
+
*/
|
|
2518
|
+
class WebSocketAsyncRequest {
|
|
2519
|
+
socket;
|
|
2520
|
+
lastId = 0;
|
|
2521
|
+
queue = [];
|
|
2522
|
+
lastRequestTime = 0;
|
|
178
2523
|
/**
|
|
179
|
-
*
|
|
2524
|
+
* Creates a new WebSocket async request handler.
|
|
2525
|
+
* @param socket - WebSocket connection instance for sending requests to the Hyperliquid WebSocket API
|
|
2526
|
+
* @param hlEvents - Used to recognize Hyperliquid responses and match them with sent requests
|
|
180
2527
|
*/
|
|
181
|
-
|
|
182
|
-
|
|
2528
|
+
constructor(socket, hlEvents) {
|
|
2529
|
+
this.socket = socket;
|
|
2530
|
+
// Monitor responses and match the pending request
|
|
2531
|
+
hlEvents.addEventListener("subscriptionResponse", (event) => {
|
|
2532
|
+
// Use a stringified request as an id
|
|
2533
|
+
const id = WebSocketAsyncRequest.requestToId(event.detail);
|
|
2534
|
+
this.queue.findLast((item) => item.id === id)?.resolve(event.detail);
|
|
2535
|
+
});
|
|
2536
|
+
hlEvents.addEventListener("post", (event) => {
|
|
2537
|
+
const data = event.detail.response.type === "info"
|
|
2538
|
+
? event.detail.response.payload.data
|
|
2539
|
+
: event.detail.response.payload;
|
|
2540
|
+
this.queue.findLast((item) => item.id === event.detail.id)?.resolve(data);
|
|
2541
|
+
});
|
|
2542
|
+
hlEvents.addEventListener("pong", () => {
|
|
2543
|
+
this.queue.findLast((item) => item.id === "ping")?.resolve();
|
|
2544
|
+
});
|
|
2545
|
+
hlEvents.addEventListener("error", (event) => {
|
|
2546
|
+
try {
|
|
2547
|
+
// Error event doesn't have an id, use original request to match
|
|
2548
|
+
const request = event.detail.match(/{.*}/)?.[0];
|
|
2549
|
+
if (!request)
|
|
2550
|
+
return;
|
|
2551
|
+
const parsedRequest = JSON.parse(request);
|
|
2552
|
+
// For `post` requests
|
|
2553
|
+
if ("id" in parsedRequest && typeof parsedRequest.id === "number") {
|
|
2554
|
+
this.queue.findLast((item) => item.id === parsedRequest.id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
|
|
2555
|
+
return;
|
|
2556
|
+
}
|
|
2557
|
+
// For `subscribe` and `unsubscribe` requests
|
|
2558
|
+
if ("subscription" in parsedRequest &&
|
|
2559
|
+
typeof parsedRequest.subscription === "object" && parsedRequest.subscription !== null) {
|
|
2560
|
+
const id = WebSocketAsyncRequest.requestToId(parsedRequest);
|
|
2561
|
+
this.queue.findLast((item) => item.id === id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
// For already/invalid subscribed requests
|
|
2565
|
+
if (event.detail.startsWith("Already subscribed") || event.detail.startsWith("Invalid subscription")) {
|
|
2566
|
+
const id = WebSocketAsyncRequest.requestToId({
|
|
2567
|
+
method: "subscribe",
|
|
2568
|
+
subscription: parsedRequest,
|
|
2569
|
+
});
|
|
2570
|
+
this.queue.findLast((item) => item.id === id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
// For already unsubscribed requests
|
|
2574
|
+
if (event.detail.startsWith("Already unsubscribed")) {
|
|
2575
|
+
const id = WebSocketAsyncRequest.requestToId({
|
|
2576
|
+
method: "unsubscribe",
|
|
2577
|
+
subscription: parsedRequest,
|
|
2578
|
+
});
|
|
2579
|
+
this.queue.findLast((item) => item.id === id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
|
|
2580
|
+
return;
|
|
2581
|
+
}
|
|
2582
|
+
// For unknown requests
|
|
2583
|
+
const id = WebSocketAsyncRequest.requestToId(parsedRequest);
|
|
2584
|
+
this.queue.findLast((item) => item.id === id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
|
|
2585
|
+
}
|
|
2586
|
+
catch {
|
|
2587
|
+
// Ignore JSON parsing errors
|
|
2588
|
+
}
|
|
2589
|
+
});
|
|
2590
|
+
// Throws all pending requests if the connection is dropped
|
|
2591
|
+
socket.addEventListener("close", () => {
|
|
2592
|
+
this.queue.forEach(({ reject }) => {
|
|
2593
|
+
reject(new WebSocketRequestError("Cannot complete WebSocket request: connection is closed"));
|
|
2594
|
+
});
|
|
2595
|
+
this.queue = [];
|
|
2596
|
+
});
|
|
2597
|
+
}
|
|
2598
|
+
async request(method, payload_or_signal, maybeSignal) {
|
|
2599
|
+
const payload = payload_or_signal instanceof AbortSignal ? undefined : payload_or_signal;
|
|
2600
|
+
const signal = payload_or_signal instanceof AbortSignal ? payload_or_signal : maybeSignal;
|
|
2601
|
+
// Reject the request if the signal is aborted
|
|
2602
|
+
if (signal?.aborted)
|
|
2603
|
+
return Promise.reject(signal.reason);
|
|
2604
|
+
// Create a request
|
|
2605
|
+
let id;
|
|
2606
|
+
let request;
|
|
2607
|
+
if (method === "post") {
|
|
2608
|
+
id = ++this.lastId;
|
|
2609
|
+
request = { method, id, request: payload };
|
|
2610
|
+
}
|
|
2611
|
+
else if (method === "ping") {
|
|
2612
|
+
id = "ping";
|
|
2613
|
+
request = { method };
|
|
2614
|
+
}
|
|
2615
|
+
else {
|
|
2616
|
+
request = { method, subscription: payload };
|
|
2617
|
+
id = WebSocketAsyncRequest.requestToId(request);
|
|
2618
|
+
}
|
|
2619
|
+
// Send the request
|
|
2620
|
+
this.socket.send(JSON.stringify(request), signal);
|
|
2621
|
+
this.lastRequestTime = Date.now();
|
|
2622
|
+
// Wait for a response
|
|
2623
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
2624
|
+
this.queue.push({ id, resolve, reject });
|
|
2625
|
+
const onAbort = () => reject(signal?.reason);
|
|
2626
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2627
|
+
return await promise.finally(() => {
|
|
2628
|
+
const index = this.queue.findLastIndex((item) => item.id === id);
|
|
2629
|
+
if (index !== -1)
|
|
2630
|
+
this.queue.splice(index, 1);
|
|
2631
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2632
|
+
});
|
|
2633
|
+
}
|
|
2634
|
+
/** Normalizes an object and then converts it to a string. */
|
|
2635
|
+
static requestToId(value) {
|
|
2636
|
+
const lowerHex = containsUppercaseHex(value) ? deepLowerHex(value) : value;
|
|
2637
|
+
const sorted = deepSortKeys(lowerHex);
|
|
2638
|
+
return JSON.stringify(sorted); // Also removes undefined
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
/** Deeply converts hexadecimal strings in an object/array to lowercase. */
|
|
2642
|
+
function deepLowerHex(obj) {
|
|
2643
|
+
if (typeof obj === "string") {
|
|
2644
|
+
return /^(0X[0-9a-fA-F]*|0x[0-9a-fA-F]*[A-F][0-9a-fA-F]*)$/.test(obj) ? obj.toLowerCase() : obj;
|
|
2645
|
+
}
|
|
2646
|
+
if (Array.isArray(obj)) {
|
|
2647
|
+
return obj.map(deepLowerHex);
|
|
2648
|
+
}
|
|
2649
|
+
if (typeof obj === "object" && obj !== null) {
|
|
2650
|
+
const result = {};
|
|
2651
|
+
const entries = Object.entries(obj);
|
|
2652
|
+
for (const [key, value] of entries) {
|
|
2653
|
+
result[key] = deepLowerHex(value);
|
|
2654
|
+
}
|
|
2655
|
+
return result;
|
|
2656
|
+
}
|
|
2657
|
+
return obj;
|
|
2658
|
+
}
|
|
2659
|
+
/** Check if an object contains uppercase hexadecimal strings. */
|
|
2660
|
+
function containsUppercaseHex(obj) {
|
|
2661
|
+
const str = JSON.stringify(obj);
|
|
2662
|
+
return /0X[0-9a-fA-F]*|0x[0-9a-fA-F]*[A-F][0-9a-fA-F]*/.test(str);
|
|
2663
|
+
}
|
|
2664
|
+
/** Deeply sort the keys of an object. */
|
|
2665
|
+
function deepSortKeys(obj) {
|
|
2666
|
+
if (typeof obj !== "object" || obj === null) {
|
|
2667
|
+
return obj;
|
|
2668
|
+
}
|
|
2669
|
+
if (Array.isArray(obj)) {
|
|
2670
|
+
return obj.map(deepSortKeys);
|
|
2671
|
+
}
|
|
2672
|
+
const result = {};
|
|
2673
|
+
const keys = Object.keys(obj).sort();
|
|
2674
|
+
for (const key of keys) {
|
|
2675
|
+
result[key] = deepSortKeys(obj[key]);
|
|
2676
|
+
}
|
|
2677
|
+
return result;
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
/** WebSocket implementation of the REST and Subscription transport interfaces. */
|
|
2681
|
+
class WebSocketTransport {
|
|
2682
|
+
_wsRequester;
|
|
2683
|
+
_hlEvents;
|
|
2684
|
+
_keepAliveTimeout = null;
|
|
2685
|
+
_subscriptions = new Map();
|
|
2686
|
+
_isReconnecting = false;
|
|
2687
|
+
/**
|
|
2688
|
+
* Request timeout in ms.
|
|
2689
|
+
* Set to `null` to disable.
|
|
2690
|
+
*/
|
|
2691
|
+
timeout;
|
|
2692
|
+
/** Keep-alive configuration. */
|
|
2693
|
+
keepAlive;
|
|
2694
|
+
/** Enable automatic resubscription after reconnection. */
|
|
2695
|
+
autoResubscribe;
|
|
2696
|
+
/** The WebSocket that is used for communication. */
|
|
2697
|
+
socket;
|
|
2698
|
+
/**
|
|
2699
|
+
* Creates a new WebSocket transport instance.
|
|
2700
|
+
* @param options - Configuration options for the WebSocket transport layer.
|
|
2701
|
+
*/
|
|
2702
|
+
constructor(options) {
|
|
2703
|
+
this.socket = new ReconnectingWebSocket(options?.url ?? "wss://api.hyperliquid.xyz/ws", undefined, options?.reconnect);
|
|
2704
|
+
this._hlEvents = new HyperliquidEventTarget(this.socket);
|
|
2705
|
+
this._wsRequester = new WebSocketAsyncRequest(this.socket, this._hlEvents);
|
|
2706
|
+
this.timeout = options?.timeout === undefined ? 10_000 : options.timeout;
|
|
2707
|
+
this.keepAlive = {
|
|
2708
|
+
interval: options?.keepAlive?.interval === undefined ? 30_000 : options.keepAlive?.interval,
|
|
2709
|
+
timeout: options?.keepAlive?.timeout === undefined ? this.timeout : options.keepAlive?.timeout,
|
|
2710
|
+
};
|
|
2711
|
+
this.autoResubscribe = options?.autoResubscribe ?? true;
|
|
2712
|
+
// Initialize listeners
|
|
2713
|
+
this.socket.addEventListener("open", () => {
|
|
2714
|
+
this._keepAliveStart();
|
|
2715
|
+
this._resubscribeStart();
|
|
2716
|
+
});
|
|
2717
|
+
this.socket.addEventListener("close", () => {
|
|
2718
|
+
this._keepAliveStop();
|
|
2719
|
+
this._resubscribeStop();
|
|
2720
|
+
this._isReconnecting = true;
|
|
2721
|
+
});
|
|
2722
|
+
}
|
|
2723
|
+
/**
|
|
2724
|
+
* Sends a request to the Hyperliquid API via WebSocket.
|
|
2725
|
+
*
|
|
2726
|
+
* Note: Explorer requests are not supported in the Hyperliquid WebSocket API.
|
|
2727
|
+
*
|
|
2728
|
+
* @param endpoint - The API endpoint to send the request to (`explorer` requests are not supported).
|
|
2729
|
+
* @param payload - The payload to send with the request.
|
|
2730
|
+
* @param signal - An optional abort signal.
|
|
2731
|
+
* @returns A promise that resolves with parsed JSON response body.
|
|
2732
|
+
* @throws {WebSocketRequestError} - An error that occurs when a WebSocket request fails.
|
|
2733
|
+
*/
|
|
2734
|
+
request(type, payload, signal) {
|
|
2735
|
+
const timeoutSignal = this.timeout ? AbortSignal.timeout(this.timeout) : undefined;
|
|
2736
|
+
const combinedSignal = signal && timeoutSignal
|
|
2737
|
+
? AbortSignal.any([signal, timeoutSignal])
|
|
2738
|
+
: signal ?? timeoutSignal;
|
|
2739
|
+
return this._wsRequester.request("post", {
|
|
2740
|
+
type: type === "exchange" ? "action" : type,
|
|
2741
|
+
payload,
|
|
2742
|
+
}, combinedSignal);
|
|
2743
|
+
}
|
|
2744
|
+
/**
|
|
2745
|
+
* Subscribes to a Hyperliquid event channel.
|
|
2746
|
+
*
|
|
2747
|
+
* Sends a subscription request to the server and listens for events.
|
|
2748
|
+
*
|
|
2749
|
+
* @param channel - The event channel to listen to.
|
|
2750
|
+
* @param payload - A payload to send with the subscription request.
|
|
2751
|
+
* @param listener - A function to call when the event is dispatched.
|
|
2752
|
+
* @returns A promise that resolves with a {@link Subscription} object to manage the subscription lifecycle.
|
|
2753
|
+
* @throws {WebSocketRequestError} - An error that occurs when a WebSocket request fails.
|
|
2754
|
+
*/
|
|
2755
|
+
async subscribe(channel, payload, listener) {
|
|
2756
|
+
// Create a unique identifier for the subscription
|
|
2757
|
+
const id = WebSocketAsyncRequest.requestToId(payload);
|
|
2758
|
+
// Initialize new subscription, if it doesn't exist
|
|
2759
|
+
let subscription = this._subscriptions.get(id);
|
|
2760
|
+
if (!subscription) {
|
|
2761
|
+
// Send subscription request
|
|
2762
|
+
const promise = this._wsRequester.request("subscribe", payload);
|
|
2763
|
+
// Cache subscription info
|
|
2764
|
+
subscription = {
|
|
2765
|
+
listeners: new Map(),
|
|
2766
|
+
promise,
|
|
2767
|
+
resubscribeAbortController: new AbortController(),
|
|
2768
|
+
};
|
|
2769
|
+
this._subscriptions.set(id, subscription);
|
|
2770
|
+
}
|
|
2771
|
+
// Initialize new listener, if it doesn't exist
|
|
2772
|
+
let unsubscribe = subscription.listeners.get(listener);
|
|
2773
|
+
if (!unsubscribe) {
|
|
2774
|
+
// Create new unsubscribe function
|
|
2775
|
+
unsubscribe = async () => {
|
|
2776
|
+
// Remove listener and cleanup
|
|
2777
|
+
this._hlEvents.removeEventListener(channel, listener);
|
|
2778
|
+
const subscription = this._subscriptions.get(id);
|
|
2779
|
+
subscription?.listeners.delete(listener);
|
|
2780
|
+
// If no listeners remain, remove subscription entirely
|
|
2781
|
+
if (subscription?.listeners.size === 0) {
|
|
2782
|
+
// Cleanup subscription
|
|
2783
|
+
this._subscriptions.delete(id);
|
|
2784
|
+
// If the socket is open, send unsubscription request
|
|
2785
|
+
if (this.socket.readyState === ReconnectingWebSocket.OPEN) {
|
|
2786
|
+
await this._wsRequester.request("unsubscribe", payload);
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
// Add listener and cache unsubscribe function
|
|
2791
|
+
this._hlEvents.addEventListener(channel, listener);
|
|
2792
|
+
subscription.listeners.set(listener, unsubscribe);
|
|
2793
|
+
}
|
|
2794
|
+
// Wait for the initial subscription request to complete
|
|
2795
|
+
await subscription.promise;
|
|
2796
|
+
// Return subscription control object
|
|
2797
|
+
return {
|
|
2798
|
+
unsubscribe,
|
|
2799
|
+
resubscribeSignal: subscription.resubscribeAbortController?.signal,
|
|
2800
|
+
};
|
|
2801
|
+
}
|
|
2802
|
+
/**
|
|
2803
|
+
* Waits until the WebSocket connection is ready.
|
|
2804
|
+
* @param signal - An optional abort signal.
|
|
2805
|
+
* @returns A promise that resolves when the connection is ready.
|
|
2806
|
+
*/
|
|
2807
|
+
ready(signal) {
|
|
2808
|
+
return new Promise((resolve, reject) => {
|
|
2809
|
+
const combinedSignal = signal
|
|
2810
|
+
? AbortSignal.any([this.socket.reconnectAbortController.signal, signal])
|
|
2811
|
+
: this.socket.reconnectAbortController.signal;
|
|
2812
|
+
if (combinedSignal.aborted)
|
|
2813
|
+
return reject(combinedSignal.reason);
|
|
2814
|
+
if (this.socket.readyState === ReconnectingWebSocket.OPEN)
|
|
2815
|
+
return resolve();
|
|
2816
|
+
const handleOpen = () => {
|
|
2817
|
+
combinedSignal.removeEventListener("abort", handleAbort);
|
|
2818
|
+
resolve();
|
|
2819
|
+
};
|
|
2820
|
+
const handleAbort = () => {
|
|
2821
|
+
this.socket.removeEventListener("open", handleOpen);
|
|
2822
|
+
reject(combinedSignal.reason);
|
|
2823
|
+
};
|
|
2824
|
+
this.socket.addEventListener("open", handleOpen, { once: true });
|
|
2825
|
+
combinedSignal.addEventListener("abort", handleAbort, { once: true });
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
/**
|
|
2829
|
+
* Closes the WebSocket connection and waits until it is fully closed.
|
|
2830
|
+
* @param signal - An optional abort signal.
|
|
2831
|
+
* @returns A promise that resolves when the connection is fully closed.
|
|
2832
|
+
*/
|
|
2833
|
+
close(signal) {
|
|
2834
|
+
return new Promise((resolve, reject) => {
|
|
2835
|
+
if (signal?.aborted)
|
|
2836
|
+
return reject(signal.reason);
|
|
2837
|
+
if (this.socket.readyState === ReconnectingWebSocket.CLOSED)
|
|
2838
|
+
return resolve();
|
|
2839
|
+
const handleClose = () => {
|
|
2840
|
+
signal?.removeEventListener("abort", handleAbort);
|
|
2841
|
+
resolve();
|
|
2842
|
+
};
|
|
2843
|
+
const handleAbort = () => {
|
|
2844
|
+
this.socket.removeEventListener("close", handleClose);
|
|
2845
|
+
reject(signal?.reason);
|
|
2846
|
+
};
|
|
2847
|
+
this.socket.addEventListener("close", handleClose, { once: true });
|
|
2848
|
+
signal?.addEventListener("abort", handleAbort, { once: true });
|
|
2849
|
+
this.socket.close();
|
|
2850
|
+
});
|
|
2851
|
+
}
|
|
2852
|
+
/** Keep the connection alive. Sends ping only when necessary. */
|
|
2853
|
+
_keepAliveStart() {
|
|
2854
|
+
if (this.keepAlive.interval === null || this._keepAliveTimeout)
|
|
2855
|
+
return;
|
|
2856
|
+
const tick = async () => {
|
|
2857
|
+
if (this.socket.readyState !== ReconnectingWebSocket.OPEN || !this._keepAliveTimeout ||
|
|
2858
|
+
this.keepAlive.interval === null)
|
|
2859
|
+
return;
|
|
2860
|
+
// Check if the last request was sent more than the keep-alive interval ago
|
|
2861
|
+
if (Date.now() - this._wsRequester.lastRequestTime >= this.keepAlive.interval) {
|
|
2862
|
+
const timeoutSignal = this.keepAlive.timeout ? AbortSignal.timeout(this.keepAlive.timeout) : undefined;
|
|
2863
|
+
await this._wsRequester.request("ping", timeoutSignal)
|
|
2864
|
+
.catch(() => undefined); // Ignore errors
|
|
2865
|
+
}
|
|
2866
|
+
// Schedule the next ping
|
|
2867
|
+
if (this.socket.readyState === ReconnectingWebSocket.OPEN && this._keepAliveTimeout &&
|
|
2868
|
+
this.keepAlive.interval !== null) {
|
|
2869
|
+
const nextDelay = this.keepAlive.interval - (Date.now() - this._wsRequester.lastRequestTime);
|
|
2870
|
+
this._keepAliveTimeout = setTimeout(tick, nextDelay);
|
|
2871
|
+
}
|
|
2872
|
+
};
|
|
2873
|
+
this._keepAliveTimeout = setTimeout(tick, this.keepAlive.interval);
|
|
2874
|
+
}
|
|
2875
|
+
_keepAliveStop() {
|
|
2876
|
+
if (this._keepAliveTimeout !== null) {
|
|
2877
|
+
clearTimeout(this._keepAliveTimeout);
|
|
2878
|
+
this._keepAliveTimeout = null;
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
/** Resubscribe to all existing subscriptions if auto-resubscribe is enabled. */
|
|
2882
|
+
_resubscribeStart() {
|
|
2883
|
+
if (this.autoResubscribe && this._isReconnecting) {
|
|
2884
|
+
for (const [id, subscriptionInfo] of this._subscriptions.entries()) {
|
|
2885
|
+
subscriptionInfo.promise = this._wsRequester.request("subscribe", JSON.parse(id))
|
|
2886
|
+
.catch((error) => {
|
|
2887
|
+
subscriptionInfo.resubscribeAbortController?.abort(error);
|
|
2888
|
+
});
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
_resubscribeStop() {
|
|
2893
|
+
if (!this.autoResubscribe || this.socket.reconnectAbortController.signal.aborted) {
|
|
2894
|
+
for (const subscriptionInfo of this._subscriptions.values()) {
|
|
2895
|
+
for (const [_, unsubscribe] of subscriptionInfo.listeners) {
|
|
2896
|
+
unsubscribe(); // does not cause an error if used when the connection is closed
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
async [Symbol.asyncDispose]() {
|
|
2902
|
+
await this.close();
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
/**
|
|
2907
|
+
* Hyperliquid service client for direct API communication
|
|
2908
|
+
*/
|
|
2909
|
+
class HyperliquidService {
|
|
2910
|
+
constructor(wsUrl = 'wss://api.hyperliquid.xyz/ws', isTestnet = false) {
|
|
2911
|
+
// Market data
|
|
2912
|
+
this.allMidsData = null;
|
|
2913
|
+
this.webData2 = null;
|
|
2914
|
+
// Optimized caches
|
|
2915
|
+
this.assetBySymbol = new Map();
|
|
2916
|
+
this.allAssetsCache = null;
|
|
2917
|
+
// Initialize HTTP client
|
|
2918
|
+
this.infoClient = new InfoClient({
|
|
2919
|
+
transport: new HttpTransport(),
|
|
2920
|
+
});
|
|
2921
|
+
// Initialize WebSocket if URL provided
|
|
2922
|
+
if (wsUrl) {
|
|
2923
|
+
this.initializeWebSocket(wsUrl);
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
initializeWebSocket(wsUrl) {
|
|
2927
|
+
this.wsTransport = new WebSocketTransport({
|
|
2928
|
+
url: wsUrl,
|
|
2929
|
+
});
|
|
2930
|
+
this.subsClient = new SubscriptionClient({ transport: this.wsTransport });
|
|
2931
|
+
// Subscribe to all mids for real-time price data
|
|
2932
|
+
this.subsClient.allMids((data) => {
|
|
2933
|
+
this.allMidsData = data;
|
|
2934
|
+
});
|
|
2935
|
+
// Subscribe to web data for asset information
|
|
2936
|
+
// Will be updated when address is set via updateUserAddress method
|
|
2937
|
+
}
|
|
2938
|
+
/**
|
|
2939
|
+
* Refresh asset cache when webData2 updates
|
|
2940
|
+
*/
|
|
2941
|
+
refreshAssetCache() {
|
|
2942
|
+
var _a, _b;
|
|
2943
|
+
if (!((_a = this.webData2) === null || _a === void 0 ? void 0 : _a.meta.universe) || !((_b = this.webData2) === null || _b === void 0 ? void 0 : _b.assetCtxs))
|
|
2944
|
+
return;
|
|
2945
|
+
this.assetBySymbol.clear();
|
|
2946
|
+
this.allAssetsCache = null;
|
|
2947
|
+
// Build optimized lookup map
|
|
2948
|
+
this.webData2.meta.universe.forEach((universe, index) => {
|
|
2949
|
+
const assetCtx = this.webData2.assetCtxs[index];
|
|
2950
|
+
if (assetCtx) {
|
|
2951
|
+
this.assetBySymbol.set(universe.name, {
|
|
2952
|
+
universe,
|
|
2953
|
+
assetCtx,
|
|
2954
|
+
index
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2957
|
+
});
|
|
2958
|
+
}
|
|
2959
|
+
/**
|
|
2960
|
+
* Get asset information by symbol - O(1) lookup
|
|
2961
|
+
*/
|
|
2962
|
+
getAssetInfo(symbol) {
|
|
2963
|
+
var _a;
|
|
2964
|
+
return ((_a = this.assetBySymbol.get(symbol)) === null || _a === void 0 ? void 0 : _a.universe) || null;
|
|
2965
|
+
}
|
|
2966
|
+
/**
|
|
2967
|
+
* Get asset context by symbol - O(1) lookup
|
|
2968
|
+
*/
|
|
2969
|
+
getAssetCtx(symbol) {
|
|
2970
|
+
var _a;
|
|
2971
|
+
return ((_a = this.assetBySymbol.get(symbol)) === null || _a === void 0 ? void 0 : _a.assetCtx) || null;
|
|
2972
|
+
}
|
|
2973
|
+
/**
|
|
2974
|
+
* Get asset index by symbol - O(1) lookup
|
|
2975
|
+
*/
|
|
2976
|
+
getAssetIndex(symbol) {
|
|
2977
|
+
const data = this.assetBySymbol.get(symbol);
|
|
2978
|
+
return data ? data.index : null;
|
|
2979
|
+
}
|
|
2980
|
+
/**
|
|
2981
|
+
* Get all assets with caching
|
|
2982
|
+
*/
|
|
2983
|
+
getAllAssets() {
|
|
2984
|
+
if (this.allAssetsCache) {
|
|
2985
|
+
return this.allAssetsCache;
|
|
2986
|
+
}
|
|
2987
|
+
this.allAssetsCache = Array.from(this.assetBySymbol.values()).map(data => ({
|
|
2988
|
+
universe: data.universe,
|
|
2989
|
+
assetsCtx: data.assetCtx,
|
|
2990
|
+
}));
|
|
2991
|
+
return this.allAssetsCache;
|
|
2992
|
+
}
|
|
2993
|
+
/**
|
|
2994
|
+
* Get current market price for an asset
|
|
2995
|
+
*/
|
|
2996
|
+
getMarketPrice(symbol) {
|
|
2997
|
+
var _a;
|
|
2998
|
+
return parseFloat(((_a = this.allMidsData) === null || _a === void 0 ? void 0 : _a.mids[symbol]) || '0');
|
|
2999
|
+
}
|
|
3000
|
+
/**
|
|
3001
|
+
* Get optimal decimal places for an asset
|
|
3002
|
+
*/
|
|
3003
|
+
getOptimalDecimal(symbol) {
|
|
3004
|
+
const asset = this.getAssetInfo(symbol);
|
|
3005
|
+
return asset ? asset.szDecimals : 8;
|
|
3006
|
+
}
|
|
3007
|
+
/**
|
|
3008
|
+
* Get user's open positions from Hyperliquid
|
|
3009
|
+
*/
|
|
3010
|
+
async getUserPositions(address) {
|
|
3011
|
+
try {
|
|
3012
|
+
const state = await this.infoClient.clearinghouseState({
|
|
3013
|
+
user: address
|
|
3014
|
+
});
|
|
3015
|
+
return state.assetPositions || [];
|
|
3016
|
+
}
|
|
3017
|
+
catch (error) {
|
|
3018
|
+
console.error('Failed to get user positions:', error);
|
|
3019
|
+
return [];
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
/**
|
|
3023
|
+
* Get user's account summary from Hyperliquid
|
|
3024
|
+
*/
|
|
3025
|
+
async getAccountSummary(address) {
|
|
3026
|
+
try {
|
|
3027
|
+
return await this.infoClient.clearinghouseState({
|
|
3028
|
+
user: address
|
|
3029
|
+
});
|
|
3030
|
+
}
|
|
3031
|
+
catch (error) {
|
|
3032
|
+
console.error('Failed to get account summary:', error);
|
|
3033
|
+
return null;
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
/**
|
|
3037
|
+
* Get user's open orders from Hyperliquid
|
|
3038
|
+
*/
|
|
3039
|
+
async getUserOrders(address) {
|
|
3040
|
+
try {
|
|
3041
|
+
return await this.infoClient.openOrders({
|
|
3042
|
+
user: address
|
|
3043
|
+
});
|
|
3044
|
+
}
|
|
3045
|
+
catch (error) {
|
|
3046
|
+
console.error('Failed to get user orders:', error);
|
|
3047
|
+
return [];
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
/**
|
|
3051
|
+
* Update the user address for webData2 subscription
|
|
3052
|
+
*/
|
|
3053
|
+
updateUserAddress(address) {
|
|
3054
|
+
if (this.subsClient && address) {
|
|
3055
|
+
// Subscribe to webData2 with the new address
|
|
3056
|
+
this.subsClient.webData2({
|
|
3057
|
+
user: address,
|
|
3058
|
+
}, (data) => {
|
|
3059
|
+
this.webData2 = data;
|
|
3060
|
+
this.refreshAssetCache();
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
/**
|
|
3065
|
+
* Get the info client instance
|
|
3066
|
+
*/
|
|
3067
|
+
getInfoClient() {
|
|
3068
|
+
return this.infoClient;
|
|
3069
|
+
}
|
|
3070
|
+
/**
|
|
3071
|
+
* Check if WebSocket is connected
|
|
3072
|
+
*/
|
|
3073
|
+
isWebSocketConnected() {
|
|
3074
|
+
return !!this.subsClient && !!this.wsTransport;
|
|
3075
|
+
}
|
|
3076
|
+
/**
|
|
3077
|
+
* Clean up resources
|
|
3078
|
+
*/
|
|
3079
|
+
async cleanup() {
|
|
3080
|
+
if (this.wsTransport && this.wsTransport.close) {
|
|
3081
|
+
await this.wsTransport.close();
|
|
3082
|
+
}
|
|
3083
|
+
this.assetBySymbol.clear();
|
|
3084
|
+
this.allAssetsCache = null;
|
|
3085
|
+
this.allMidsData = null;
|
|
3086
|
+
this.webData2 = null;
|
|
183
3087
|
}
|
|
184
3088
|
}
|
|
185
3089
|
|
|
@@ -2613,9 +5517,11 @@ const PearHyperliquidContext = createContext(undefined);
|
|
|
2613
5517
|
/**
|
|
2614
5518
|
* React Provider for PearHyperliquidClient
|
|
2615
5519
|
*/
|
|
2616
|
-
const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/ws', children, }) => {
|
|
5520
|
+
const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/ws', hyperliquidWsUrl = 'wss://api.hyperliquid.xyz/ws', children, }) => {
|
|
2617
5521
|
const client = useMemo(() => new PearHyperliquidClient(config), [config]);
|
|
2618
5522
|
const migrationSDK = useMemo(() => new PearMigrationSDK(client), [client]);
|
|
5523
|
+
// Initialize Hyperliquid service
|
|
5524
|
+
const hyperliquidService = useMemo(() => new HyperliquidService(hyperliquidWsUrl), [hyperliquidWsUrl]);
|
|
2619
5525
|
// Address state
|
|
2620
5526
|
const [address, setAddress] = useState(null);
|
|
2621
5527
|
// WebSocket data state
|
|
@@ -2712,10 +5618,16 @@ const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/
|
|
|
2712
5618
|
});
|
|
2713
5619
|
}
|
|
2714
5620
|
}, [isConnected, address, sendMessage]);
|
|
5621
|
+
// Handle address changes for Hyperliquid service
|
|
5622
|
+
useEffect(() => {
|
|
5623
|
+
hyperliquidService.updateUserAddress(address);
|
|
5624
|
+
}, [address, hyperliquidService]);
|
|
2715
5625
|
const contextValue = useMemo(() => ({
|
|
2716
5626
|
// Existing clients
|
|
2717
5627
|
client,
|
|
2718
5628
|
migrationSDK,
|
|
5629
|
+
// Enhanced services
|
|
5630
|
+
hyperliquidService,
|
|
2719
5631
|
// Address management
|
|
2720
5632
|
address,
|
|
2721
5633
|
setAddress,
|
|
@@ -2725,7 +5637,7 @@ const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/
|
|
|
2725
5637
|
// WebSocket data
|
|
2726
5638
|
data,
|
|
2727
5639
|
lastError,
|
|
2728
|
-
}), [client, migrationSDK, address, readyState, isConnected, data, lastError]);
|
|
5640
|
+
}), [client, migrationSDK, hyperliquidService, address, readyState, isConnected, data, lastError]);
|
|
2729
5641
|
return (jsxRuntimeExports.jsx(PearHyperliquidContext.Provider, { value: contextValue, children: children }));
|
|
2730
5642
|
};
|
|
2731
5643
|
/**
|
|
@@ -2748,6 +5660,16 @@ const useMigrationSDK = () => {
|
|
|
2748
5660
|
}
|
|
2749
5661
|
return context.migrationSDK;
|
|
2750
5662
|
};
|
|
5663
|
+
/**
|
|
5664
|
+
* Hook to use Hyperliquid service from context
|
|
5665
|
+
*/
|
|
5666
|
+
const useHyperliquidService = () => {
|
|
5667
|
+
const context = useContext(PearHyperliquidContext);
|
|
5668
|
+
if (!context) {
|
|
5669
|
+
throw new Error('useHyperliquidService must be used within a PearHyperliquidProvider');
|
|
5670
|
+
}
|
|
5671
|
+
return context.hyperliquidService;
|
|
5672
|
+
};
|
|
2751
5673
|
|
|
2752
5674
|
/**
|
|
2753
5675
|
* Hook to manage address (login/logout functionality)
|
|
@@ -2776,14 +5698,74 @@ const useTradeHistories = () => {
|
|
|
2776
5698
|
return context.data.tradeHistories;
|
|
2777
5699
|
};
|
|
2778
5700
|
/**
|
|
2779
|
-
* Hook to access open positions
|
|
5701
|
+
* Hook to access open positions with enhanced real-time synchronization
|
|
2780
5702
|
*/
|
|
2781
5703
|
const useOpenPositions = () => {
|
|
2782
5704
|
const context = useContext(PearHyperliquidContext);
|
|
5705
|
+
const hyperliquidService = useHyperliquidService();
|
|
5706
|
+
const [enhancedPositions, setEnhancedPositions] = useState([]);
|
|
5707
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
5708
|
+
const [lastSyncTime, setLastSyncTime] = useState(null);
|
|
2783
5709
|
if (!context) {
|
|
2784
5710
|
throw new Error('useOpenPositions must be used within a PearHyperliquidProvider');
|
|
2785
5711
|
}
|
|
2786
|
-
|
|
5712
|
+
// Get base positions from WebSocket
|
|
5713
|
+
const wsPositions = context.data.openPositions;
|
|
5714
|
+
// Enhance positions with real-time Hyperliquid data
|
|
5715
|
+
const syncPositions = useCallback(async () => {
|
|
5716
|
+
if (!wsPositions || wsPositions.length === 0) {
|
|
5717
|
+
setEnhancedPositions([]);
|
|
5718
|
+
return;
|
|
5719
|
+
}
|
|
5720
|
+
setIsLoading(true);
|
|
5721
|
+
try {
|
|
5722
|
+
// Enhanced positions with real-time market prices from Hyperliquid
|
|
5723
|
+
const enhancedPositionsData = wsPositions.map(wsPos => {
|
|
5724
|
+
// Calculate updated position values using current market prices
|
|
5725
|
+
const enhancedLongAssets = wsPos.longAssets.map(asset => ({
|
|
5726
|
+
...asset,
|
|
5727
|
+
positionValue: asset.platformSize * hyperliquidService.getMarketPrice(asset.coin),
|
|
5728
|
+
}));
|
|
5729
|
+
const enhancedShortAssets = wsPos.shortAssets.map(asset => ({
|
|
5730
|
+
...asset,
|
|
5731
|
+
positionValue: asset.platformSize * hyperliquidService.getMarketPrice(asset.coin),
|
|
5732
|
+
}));
|
|
5733
|
+
// Calculate updated position value and mark ratio
|
|
5734
|
+
const totalPositionValue = enhancedLongAssets.reduce((sum, asset) => sum + asset.positionValue, 0) +
|
|
5735
|
+
enhancedShortAssets.reduce((sum, asset) => sum + asset.positionValue, 0);
|
|
5736
|
+
return {
|
|
5737
|
+
...wsPos,
|
|
5738
|
+
longAssets: enhancedLongAssets,
|
|
5739
|
+
shortAssets: enhancedShortAssets,
|
|
5740
|
+
positionValue: totalPositionValue,
|
|
5741
|
+
// Add timestamp for when we enhanced with Hyperliquid data
|
|
5742
|
+
lastSyncAt: new Date().toISOString(),
|
|
5743
|
+
};
|
|
5744
|
+
});
|
|
5745
|
+
setEnhancedPositions(enhancedPositionsData);
|
|
5746
|
+
setLastSyncTime(new Date());
|
|
5747
|
+
}
|
|
5748
|
+
catch (error) {
|
|
5749
|
+
console.error('Failed to enhance positions with Hyperliquid data:', error);
|
|
5750
|
+
// Fall back to WebSocket data if enhancement fails
|
|
5751
|
+
setEnhancedPositions(wsPositions || []);
|
|
5752
|
+
}
|
|
5753
|
+
finally {
|
|
5754
|
+
setIsLoading(false);
|
|
5755
|
+
}
|
|
5756
|
+
}, [wsPositions, hyperliquidService]);
|
|
5757
|
+
// Sync positions whenever WebSocket data changes
|
|
5758
|
+
useEffect(() => {
|
|
5759
|
+
syncPositions();
|
|
5760
|
+
}, [syncPositions]);
|
|
5761
|
+
// Real-time updates are handled automatically through webData2 subscription
|
|
5762
|
+
return {
|
|
5763
|
+
positions: enhancedPositions, // Returns OpenPositionDto[] - fully backward compatible
|
|
5764
|
+
isLoading,
|
|
5765
|
+
lastSyncTime,
|
|
5766
|
+
rawWsPositions: wsPositions, // Expose raw WebSocket data for debugging
|
|
5767
|
+
resync: syncPositions // Manual resync function
|
|
5768
|
+
};
|
|
2787
5769
|
};
|
|
2788
5770
|
/**
|
|
2789
5771
|
* Hook to access open orders
|
|
@@ -2796,15 +5778,85 @@ const useOpenOrders = () => {
|
|
|
2796
5778
|
return context.data.openOrders;
|
|
2797
5779
|
};
|
|
2798
5780
|
/**
|
|
2799
|
-
* Hook to access account summary
|
|
5781
|
+
* Hook to access account summary with enhanced real-time data from Hyperliquid
|
|
2800
5782
|
*/
|
|
2801
5783
|
const useAccountSummary = () => {
|
|
2802
5784
|
const context = useContext(PearHyperliquidContext);
|
|
5785
|
+
const hyperliquidService = useHyperliquidService();
|
|
5786
|
+
const [enhancedSummary, setEnhancedSummary] = useState(null);
|
|
5787
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
5788
|
+
const [lastSyncTime, setLastSyncTime] = useState(null);
|
|
2803
5789
|
if (!context) {
|
|
2804
5790
|
throw new Error('useAccountSummary must be used within a PearHyperliquidProvider');
|
|
2805
5791
|
}
|
|
2806
|
-
|
|
5792
|
+
// Get base summary from WebSocket (your backend data)
|
|
5793
|
+
const wsSummary = context.data.accountSummary;
|
|
5794
|
+
// Enhance account summary with fresh Hyperliquid data
|
|
5795
|
+
const syncAccountSummary = useCallback(async () => {
|
|
5796
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
5797
|
+
if (!context.address || !wsSummary) {
|
|
5798
|
+
setEnhancedSummary(wsSummary);
|
|
5799
|
+
return;
|
|
5800
|
+
}
|
|
5801
|
+
setIsLoading(true);
|
|
5802
|
+
try {
|
|
5803
|
+
// Get fresh data from Hyperliquid (like your backend does)
|
|
5804
|
+
const hlAccountSummary = await hyperliquidService.getAccountSummary(context.address);
|
|
5805
|
+
if (hlAccountSummary) {
|
|
5806
|
+
// Enhance your backend data with fresh Hyperliquid data
|
|
5807
|
+
const enhancedSummaryData = {
|
|
5808
|
+
balanceSummary: {
|
|
5809
|
+
// Use fresh Hyperliquid data for real-time accuracy
|
|
5810
|
+
crossMaintenanceMarginUsed: hlAccountSummary.crossMaintenanceMarginUsed || wsSummary.balanceSummary.crossMaintenanceMarginUsed,
|
|
5811
|
+
crossMarginSummary: {
|
|
5812
|
+
accountValue: ((_a = hlAccountSummary.crossMarginSummary) === null || _a === void 0 ? void 0 : _a.accountValue) || wsSummary.balanceSummary.crossMarginSummary.accountValue,
|
|
5813
|
+
totalMarginUsed: ((_b = hlAccountSummary.crossMarginSummary) === null || _b === void 0 ? void 0 : _b.totalMarginUsed) || wsSummary.balanceSummary.crossMarginSummary.totalMarginUsed,
|
|
5814
|
+
totalNtlPos: ((_c = hlAccountSummary.crossMarginSummary) === null || _c === void 0 ? void 0 : _c.totalNtlPos) || wsSummary.balanceSummary.crossMarginSummary.totalNtlPos,
|
|
5815
|
+
totalRawUsd: ((_d = hlAccountSummary.crossMarginSummary) === null || _d === void 0 ? void 0 : _d.totalRawUsd) || wsSummary.balanceSummary.crossMarginSummary.totalRawUsd
|
|
5816
|
+
},
|
|
5817
|
+
marginSummary: {
|
|
5818
|
+
accountValue: ((_e = hlAccountSummary.marginSummary) === null || _e === void 0 ? void 0 : _e.accountValue) || wsSummary.balanceSummary.marginSummary.accountValue,
|
|
5819
|
+
totalMarginUsed: ((_f = hlAccountSummary.marginSummary) === null || _f === void 0 ? void 0 : _f.totalMarginUsed) || wsSummary.balanceSummary.marginSummary.totalMarginUsed,
|
|
5820
|
+
totalNtlPos: ((_g = hlAccountSummary.marginSummary) === null || _g === void 0 ? void 0 : _g.totalNtlPos) || wsSummary.balanceSummary.marginSummary.totalNtlPos,
|
|
5821
|
+
totalRawUsd: ((_h = hlAccountSummary.marginSummary) === null || _h === void 0 ? void 0 : _h.totalRawUsd) || wsSummary.balanceSummary.marginSummary.totalRawUsd
|
|
5822
|
+
},
|
|
5823
|
+
time: Date.now(), // Current timestamp
|
|
5824
|
+
// Keep your backend's calculated withdrawable (includes order deductions)
|
|
5825
|
+
withdrawable: wsSummary.balanceSummary.withdrawable
|
|
5826
|
+
},
|
|
5827
|
+
// Keep your backend's agent wallet data
|
|
5828
|
+
agentWallet: wsSummary.agentWallet
|
|
5829
|
+
};
|
|
5830
|
+
setEnhancedSummary(enhancedSummaryData);
|
|
5831
|
+
}
|
|
5832
|
+
else {
|
|
5833
|
+
// Fallback to WebSocket data if Hyperliquid fails
|
|
5834
|
+
setEnhancedSummary(wsSummary);
|
|
5835
|
+
}
|
|
5836
|
+
setLastSyncTime(new Date());
|
|
5837
|
+
}
|
|
5838
|
+
catch (error) {
|
|
5839
|
+
console.error('Failed to enhance account summary with Hyperliquid:', error);
|
|
5840
|
+
// Fall back to WebSocket data if enhancement fails
|
|
5841
|
+
setEnhancedSummary(wsSummary);
|
|
5842
|
+
}
|
|
5843
|
+
finally {
|
|
5844
|
+
setIsLoading(false);
|
|
5845
|
+
}
|
|
5846
|
+
}, [context.address, wsSummary, hyperliquidService]);
|
|
5847
|
+
// Sync account summary whenever WebSocket data changes
|
|
5848
|
+
useEffect(() => {
|
|
5849
|
+
syncAccountSummary();
|
|
5850
|
+
}, [syncAccountSummary]);
|
|
5851
|
+
// Real-time updates are handled automatically through webData2 subscription
|
|
5852
|
+
return {
|
|
5853
|
+
summary: enhancedSummary, // Returns AccountSummaryResponseDto - fully backward compatible
|
|
5854
|
+
isLoading,
|
|
5855
|
+
lastSyncTime,
|
|
5856
|
+
rawWsSummary: wsSummary, // Expose raw WebSocket data for debugging
|
|
5857
|
+
resync: syncAccountSummary // Manual resync function
|
|
5858
|
+
};
|
|
2807
5859
|
};
|
|
2808
5860
|
|
|
2809
|
-
export { PearHyperliquidClient, PearHyperliquidProvider, PearMigrationSDK, PearHyperliquidClient as default, useAccountSummary, useAddress, useMigrationSDK, useOpenOrders, useOpenPositions, usePearHyperliquidClient, useTradeHistories };
|
|
5861
|
+
export { HyperliquidService, PearHyperliquidClient, PearHyperliquidProvider, PearMigrationSDK, PearHyperliquidClient as default, useAccountSummary, useAddress, useHyperliquidService, useMigrationSDK, useOpenOrders, useOpenPositions, usePearHyperliquidClient, useTradeHistories };
|
|
2810
5862
|
//# sourceMappingURL=index.esm.js.map
|