@pear-protocol/hyperliquid-sdk 0.0.4 → 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 +102 -5
- package/dist/index.esm.js +3040 -8
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +3040 -6
- package/dist/index.js.map +1 -1
- 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
|
/**
|
|
@@ -203,6 +203,2890 @@ class PearMigrationSDK {
|
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
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");
|
|
2361
|
+
}
|
|
2362
|
+
/**
|
|
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.
|
|
2365
|
+
*/
|
|
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
|
+
}
|
|
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 {
|
|
2443
|
+
/**
|
|
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.
|
|
2447
|
+
*/
|
|
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";
|
|
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;
|
|
2523
|
+
/**
|
|
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
|
|
2527
|
+
*/
|
|
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;
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
|
|
206
3090
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
207
3091
|
|
|
208
3092
|
function getDefaultExportFromCjs (x) {
|
|
@@ -2633,9 +5517,11 @@ const PearHyperliquidContext = createContext(undefined);
|
|
|
2633
5517
|
/**
|
|
2634
5518
|
* React Provider for PearHyperliquidClient
|
|
2635
5519
|
*/
|
|
2636
|
-
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, }) => {
|
|
2637
5521
|
const client = useMemo(() => new PearHyperliquidClient(config), [config]);
|
|
2638
5522
|
const migrationSDK = useMemo(() => new PearMigrationSDK(client), [client]);
|
|
5523
|
+
// Initialize Hyperliquid service
|
|
5524
|
+
const hyperliquidService = useMemo(() => new HyperliquidService(hyperliquidWsUrl), [hyperliquidWsUrl]);
|
|
2639
5525
|
// Address state
|
|
2640
5526
|
const [address, setAddress] = useState(null);
|
|
2641
5527
|
// WebSocket data state
|
|
@@ -2732,10 +5618,16 @@ const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/
|
|
|
2732
5618
|
});
|
|
2733
5619
|
}
|
|
2734
5620
|
}, [isConnected, address, sendMessage]);
|
|
5621
|
+
// Handle address changes for Hyperliquid service
|
|
5622
|
+
useEffect(() => {
|
|
5623
|
+
hyperliquidService.updateUserAddress(address);
|
|
5624
|
+
}, [address, hyperliquidService]);
|
|
2735
5625
|
const contextValue = useMemo(() => ({
|
|
2736
5626
|
// Existing clients
|
|
2737
5627
|
client,
|
|
2738
5628
|
migrationSDK,
|
|
5629
|
+
// Enhanced services
|
|
5630
|
+
hyperliquidService,
|
|
2739
5631
|
// Address management
|
|
2740
5632
|
address,
|
|
2741
5633
|
setAddress,
|
|
@@ -2745,7 +5637,7 @@ const PearHyperliquidProvider = ({ config, wsUrl = 'wss://hl-v2.pearprotocol.io/
|
|
|
2745
5637
|
// WebSocket data
|
|
2746
5638
|
data,
|
|
2747
5639
|
lastError,
|
|
2748
|
-
}), [client, migrationSDK, address, readyState, isConnected, data, lastError]);
|
|
5640
|
+
}), [client, migrationSDK, hyperliquidService, address, readyState, isConnected, data, lastError]);
|
|
2749
5641
|
return (jsxRuntimeExports.jsx(PearHyperliquidContext.Provider, { value: contextValue, children: children }));
|
|
2750
5642
|
};
|
|
2751
5643
|
/**
|
|
@@ -2768,6 +5660,16 @@ const useMigrationSDK = () => {
|
|
|
2768
5660
|
}
|
|
2769
5661
|
return context.migrationSDK;
|
|
2770
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
|
+
};
|
|
2771
5673
|
|
|
2772
5674
|
/**
|
|
2773
5675
|
* Hook to manage address (login/logout functionality)
|
|
@@ -2796,14 +5698,74 @@ const useTradeHistories = () => {
|
|
|
2796
5698
|
return context.data.tradeHistories;
|
|
2797
5699
|
};
|
|
2798
5700
|
/**
|
|
2799
|
-
* Hook to access open positions
|
|
5701
|
+
* Hook to access open positions with enhanced real-time synchronization
|
|
2800
5702
|
*/
|
|
2801
5703
|
const useOpenPositions = () => {
|
|
2802
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);
|
|
2803
5709
|
if (!context) {
|
|
2804
5710
|
throw new Error('useOpenPositions must be used within a PearHyperliquidProvider');
|
|
2805
5711
|
}
|
|
2806
|
-
|
|
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
|
+
};
|
|
2807
5769
|
};
|
|
2808
5770
|
/**
|
|
2809
5771
|
* Hook to access open orders
|
|
@@ -2816,15 +5778,85 @@ const useOpenOrders = () => {
|
|
|
2816
5778
|
return context.data.openOrders;
|
|
2817
5779
|
};
|
|
2818
5780
|
/**
|
|
2819
|
-
* Hook to access account summary
|
|
5781
|
+
* Hook to access account summary with enhanced real-time data from Hyperliquid
|
|
2820
5782
|
*/
|
|
2821
5783
|
const useAccountSummary = () => {
|
|
2822
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);
|
|
2823
5789
|
if (!context) {
|
|
2824
5790
|
throw new Error('useAccountSummary must be used within a PearHyperliquidProvider');
|
|
2825
5791
|
}
|
|
2826
|
-
|
|
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
|
+
};
|
|
2827
5859
|
};
|
|
2828
5860
|
|
|
2829
|
-
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 };
|
|
2830
5862
|
//# sourceMappingURL=index.esm.js.map
|