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