@pear-protocol/hyperliquid-sdk 0.0.5 → 0.0.7

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