@lifi/perps-sdk-provider-hyperliquid 0.0.0-preview-c3f47bd → 0.0.0-preview-2edcdb1

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.
Files changed (116) hide show
  1. package/dist/cjs/HyperliquidProvider.d.ts.map +1 -1
  2. package/dist/cjs/HyperliquidProvider.js +2 -0
  3. package/dist/cjs/HyperliquidProvider.js.map +1 -1
  4. package/dist/cjs/accountConfig.d.ts.map +1 -1
  5. package/dist/cjs/accountConfig.js +1 -0
  6. package/dist/cjs/accountConfig.js.map +1 -1
  7. package/dist/cjs/accountSummary.d.ts.map +1 -1
  8. package/dist/cjs/accountSummary.js +5 -3
  9. package/dist/cjs/accountSummary.js.map +1 -1
  10. package/dist/cjs/index.d.ts +2 -0
  11. package/dist/cjs/index.d.ts.map +1 -1
  12. package/dist/cjs/index.js +3 -1
  13. package/dist/cjs/index.js.map +1 -1
  14. package/dist/cjs/services/getAccount.d.ts.map +1 -1
  15. package/dist/cjs/services/getAccount.js +4 -4
  16. package/dist/cjs/services/getAccount.js.map +1 -1
  17. package/dist/cjs/services/getAccountExists.d.ts +8 -0
  18. package/dist/cjs/services/getAccountExists.d.ts.map +1 -0
  19. package/dist/cjs/services/getAccountExists.js +15 -0
  20. package/dist/cjs/services/getAccountExists.js.map +1 -0
  21. package/dist/cjs/types/account.d.ts +3 -0
  22. package/dist/cjs/types/account.d.ts.map +1 -1
  23. package/dist/cjs/types/account.js.map +1 -1
  24. package/dist/cjs/types/ws.d.ts +56 -1
  25. package/dist/cjs/types/ws.d.ts.map +1 -1
  26. package/dist/cjs/utils/decodeFastAssetCtxs.d.ts +1 -0
  27. package/dist/cjs/utils/decodeFastAssetCtxs.d.ts.map +1 -1
  28. package/dist/cjs/utils/decodeFastAssetCtxs.js +5 -1
  29. package/dist/cjs/utils/decodeFastAssetCtxs.js.map +1 -1
  30. package/dist/cjs/utils/index.d.ts +1 -1
  31. package/dist/cjs/utils/index.d.ts.map +1 -1
  32. package/dist/cjs/utils/index.js +2 -1
  33. package/dist/cjs/utils/index.js.map +1 -1
  34. package/dist/cjs/utils/mapOrder.d.ts.map +1 -1
  35. package/dist/cjs/utils/mapOrder.js +12 -5
  36. package/dist/cjs/utils/mapOrder.js.map +1 -1
  37. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts +22 -0
  38. package/dist/cjs/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  39. package/dist/cjs/websocket/HyperliquidWsProvider.js +430 -28
  40. package/dist/cjs/websocket/HyperliquidWsProvider.js.map +1 -1
  41. package/dist/esm/HyperliquidProvider.d.ts.map +1 -1
  42. package/dist/esm/HyperliquidProvider.js +2 -0
  43. package/dist/esm/HyperliquidProvider.js.map +1 -1
  44. package/dist/esm/accountConfig.d.ts.map +1 -1
  45. package/dist/esm/accountConfig.js +1 -0
  46. package/dist/esm/accountConfig.js.map +1 -1
  47. package/dist/esm/accountSummary.d.ts +2 -2
  48. package/dist/esm/accountSummary.d.ts.map +1 -1
  49. package/dist/esm/accountSummary.js +12 -9
  50. package/dist/esm/accountSummary.js.map +1 -1
  51. package/dist/esm/index.d.ts +2 -0
  52. package/dist/esm/index.d.ts.map +1 -1
  53. package/dist/esm/index.js +1 -0
  54. package/dist/esm/index.js.map +1 -1
  55. package/dist/esm/services/getAccount.d.ts.map +1 -1
  56. package/dist/esm/services/getAccount.js +10 -7
  57. package/dist/esm/services/getAccount.js.map +1 -1
  58. package/dist/esm/services/getAccountExists.d.ts +22 -0
  59. package/dist/esm/services/getAccountExists.d.ts.map +1 -0
  60. package/dist/esm/services/getAccountExists.js +20 -0
  61. package/dist/esm/services/getAccountExists.js.map +1 -0
  62. package/dist/esm/types/account.d.ts +9 -0
  63. package/dist/esm/types/account.d.ts.map +1 -1
  64. package/dist/esm/types/account.js.map +1 -1
  65. package/dist/esm/types/ws.d.ts +75 -1
  66. package/dist/esm/types/ws.d.ts.map +1 -1
  67. package/dist/esm/utils/decodeFastAssetCtxs.d.ts +1 -0
  68. package/dist/esm/utils/decodeFastAssetCtxs.d.ts.map +1 -1
  69. package/dist/esm/utils/decodeFastAssetCtxs.js +9 -6
  70. package/dist/esm/utils/decodeFastAssetCtxs.js.map +1 -1
  71. package/dist/esm/utils/index.d.ts +1 -1
  72. package/dist/esm/utils/index.d.ts.map +1 -1
  73. package/dist/esm/utils/index.js +1 -1
  74. package/dist/esm/utils/index.js.map +1 -1
  75. package/dist/esm/utils/mapOrder.d.ts +11 -1
  76. package/dist/esm/utils/mapOrder.d.ts.map +1 -1
  77. package/dist/esm/utils/mapOrder.js +23 -6
  78. package/dist/esm/utils/mapOrder.js.map +1 -1
  79. package/dist/esm/websocket/HyperliquidWsProvider.d.ts +27 -8
  80. package/dist/esm/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  81. package/dist/esm/websocket/HyperliquidWsProvider.js +439 -47
  82. package/dist/esm/websocket/HyperliquidWsProvider.js.map +1 -1
  83. package/dist/types/HyperliquidProvider.d.ts.map +1 -1
  84. package/dist/types/accountConfig.d.ts.map +1 -1
  85. package/dist/types/accountSummary.d.ts +2 -2
  86. package/dist/types/accountSummary.d.ts.map +1 -1
  87. package/dist/types/index.d.ts +2 -0
  88. package/dist/types/index.d.ts.map +1 -1
  89. package/dist/types/services/getAccount.d.ts.map +1 -1
  90. package/dist/types/services/getAccountExists.d.ts +22 -0
  91. package/dist/types/services/getAccountExists.d.ts.map +1 -0
  92. package/dist/types/types/account.d.ts +9 -0
  93. package/dist/types/types/account.d.ts.map +1 -1
  94. package/dist/types/types/ws.d.ts +75 -1
  95. package/dist/types/types/ws.d.ts.map +1 -1
  96. package/dist/types/utils/decodeFastAssetCtxs.d.ts +1 -0
  97. package/dist/types/utils/decodeFastAssetCtxs.d.ts.map +1 -1
  98. package/dist/types/utils/index.d.ts +1 -1
  99. package/dist/types/utils/index.d.ts.map +1 -1
  100. package/dist/types/utils/mapOrder.d.ts +11 -1
  101. package/dist/types/utils/mapOrder.d.ts.map +1 -1
  102. package/dist/types/websocket/HyperliquidWsProvider.d.ts +27 -8
  103. package/dist/types/websocket/HyperliquidWsProvider.d.ts.map +1 -1
  104. package/package.json +3 -3
  105. package/src/HyperliquidProvider.ts +8 -0
  106. package/src/accountConfig.ts +1 -0
  107. package/src/accountSummary.ts +12 -9
  108. package/src/index.ts +2 -0
  109. package/src/services/getAccount.ts +10 -11
  110. package/src/services/getAccountExists.ts +40 -0
  111. package/src/types/account.ts +10 -0
  112. package/src/types/ws.ts +79 -1
  113. package/src/utils/decodeFastAssetCtxs.ts +10 -6
  114. package/src/utils/index.ts +4 -1
  115. package/src/utils/mapOrder.ts +23 -6
  116. package/src/websocket/HyperliquidWsProvider.ts +558 -49
@@ -1 +1 @@
1
- {"version":3,"file":"HyperliquidWsProvider.d.ts","sourceRoot":"","sources":["../../../src/websocket/HyperliquidWsProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAGlB,cAAc,EACd,KAAK,iBAAiB,EAEvB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAKL,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAA;AA2C1B,eAAO,MAAM,qBAAqB,QAC5B,iBAEgD,CAAA;AAgBtD,qBAAa,qBAAsB,SAAQ,cAAc,CAAC,MAAM,CAAC;IAC/D,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IACrD,OAAO,CAAC,eAAe,CAAsD;IAK7E,OAAO,CAAC,iBAAiB,CAAuC;IAGhE,OAAO,CAAC,eAAe,CAAmC;gBAE9C,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc;IASjE,cAAc,CAClB,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,IAAI,CAAC;cAkBN,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IAyDnE,OAAO,CAAC,oBAAoB;IAsB5B,OAAO,CAAC,2BAA2B;cAUhB,OAAO,IAAI,IAAI;IAMlC,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM9C,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM;IAmB1C,OAAO,CAAC,WAAW;IAqCnB,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM;IA4DnC,OAAO,CAAC,sBAAsB;IAiB9B,OAAO,CAAC,mBAAmB;IAuB3B,OAAO,CAAC,kBAAkB;IA6B1B,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,kBAAkB;IA0D1B,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,+BAA+B;IAkBvC,OAAO,CAAC,eAAe;CAaxB"}
1
+ {"version":3,"file":"HyperliquidWsProvider.d.ts","sourceRoot":"","sources":["../../../src/websocket/HyperliquidWsProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAGlB,cAAc,EACd,KAAK,iBAAiB,EAEvB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAOL,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAA;AAsD1B,eAAO,MAAM,qBAAqB,QAC5B,iBAEgD,CAAA;AAgBtD,qBAAa,qBAAsB,SAAQ,cAAc,CAAC,MAAM,CAAC;IAC/D,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IACrD,OAAO,CAAC,eAAe,CAAsD;IAC7E,OAAO,CAAC,iBAAiB,CAAuC;IAChE,OAAO,CAAC,uBAAuB,CAAiC;IAChE,OAAO,CAAC,yBAAyB,CAAuC;IAKxE,OAAO,CAAC,iBAAiB,CAAuC;IAGhE,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,oBAAoB,CAAmC;gBAEnD,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc;IASjE,cAAc,CAClB,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,IAAI,CAAC;cAkBN,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IAqEnE,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,iBAAiB;IAwBzB,OAAO,CAAC,2BAA2B;IAWnC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,gBAAgB;cAcL,OAAO,IAAI,IAAI;IAalC,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM9C,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM;IA0B1C,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,WAAW;IA6CnB,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM;IA8EnC,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,wBAAwB;IAsBhC,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,kBAAkB;IAuC1B,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,UAAU;IAuBlB,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,QAAQ;IAoBhB,OAAO,CAAC,oBAAoB;IA2B5B,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,kBAAkB;IA0D1B,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,+BAA+B;IAkBvC,OAAO,CAAC,eAAe;CAaxB"}
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.HyperliquidWsProvider = exports.hyperliquidWsProvider = void 0;
4
7
  const perps_sdk_1 = require("@lifi/perps-sdk");
5
8
  const perps_types_1 = require("@lifi/perps-types");
9
+ const big_js_1 = __importDefault(require("big.js"));
6
10
  const constants_js_1 = require("../constants.js");
7
11
  const index_js_1 = require("../utils/index.js");
8
12
  const HL_L2_BOOK_MAX_LEVELS_PER_SIDE = 20;
@@ -13,8 +17,14 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
13
17
  client;
14
18
  registry;
15
19
  perpCtxBySubDex = new Map();
20
+ spotCtxByMarketId = {};
21
+ orderbookKeysByMarketId = new Map();
22
+ latestOrderbookByMarketId = new Map();
16
23
  fastCtxByMarketId = {};
17
24
  fastDecodeChain = Promise.resolve();
25
+ pacDecodeChain = Promise.resolve();
26
+ sacDecodeChain = Promise.resolve();
27
+ orderbookDecodeChain = Promise.resolve();
18
28
  constructor(wsUrl, providerKey, client) {
19
29
  super(new perps_sdk_1.ReconnectingWebSocket(wsUrl, { pingPayload: '{"method":"ping"}' }), providerKey);
20
30
  this.client = client;
@@ -45,19 +55,32 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
45
55
  this.rws.send(JSON.stringify({ method: 'unsubscribe', subscription: payload }));
46
56
  }
47
57
  this.perpCtxBySubDex.clear();
58
+ this.spotCtxByMarketId = {};
48
59
  this.fastCtxByMarketId = {};
49
60
  };
50
61
  }
51
62
  const key = this.toKey(sub);
63
+ if (sub.channel === 'orderbook') {
64
+ this.claimOrderbookKey(sub.marketId, key);
65
+ }
52
66
  const payload = this.toHlPayload(sub);
53
67
  await this.registerSub(key, payload);
68
+ if (sub.channel === 'orderbook') {
69
+ this.registerOrderbook(sub.marketId, key);
70
+ }
54
71
  await this.rws.ready();
55
72
  return () => {
56
73
  this.unregisterSub(key);
57
74
  if (this.orderUpdatesKey === key) {
58
75
  this.orderUpdatesKey = undefined;
59
76
  }
60
- this.rws.send(JSON.stringify({ method: 'unsubscribe', subscription: payload }));
77
+ this.rws.send(JSON.stringify({
78
+ method: 'unsubscribe',
79
+ subscription: payload,
80
+ }));
81
+ if (sub.channel === 'orderbook') {
82
+ this.releaseOrderbook(sub.marketId, key);
83
+ }
61
84
  };
62
85
  }
63
86
  claimOrderUpdatesKey(key) {
@@ -72,15 +95,57 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
72
95
  }
73
96
  this.orderUpdatesKey = key;
74
97
  }
98
+ claimOrderbookKey(marketId, key) {
99
+ const activeKeys = this.orderbookKeysByMarketId.get(marketId);
100
+ if (activeKeys === undefined) {
101
+ return;
102
+ }
103
+ for (const activeKey of [...activeKeys]) {
104
+ if (activeKey === key) {
105
+ continue;
106
+ }
107
+ if (!this.closeChannelIfIdle(activeKey)) {
108
+ throw new Error(`Hyperliquid supports one orderbook aggregation per market per ` +
109
+ `provider instance. Unsubscribe ${activeKey} before subscribing ${key}.`);
110
+ }
111
+ }
112
+ }
75
113
  getMarketsContextSubEntries() {
76
114
  return [
77
- { subKey: 'allDexsAssetCtxs', payload: { type: 'allDexsAssetCtxs' } },
115
+ { subKey: 'pac', payload: { type: 'pac' } },
116
+ { subKey: 'sac', payload: { type: 'sac' } },
78
117
  { subKey: 'fastAssetCtxs', payload: { type: 'fastAssetCtxs' } },
79
118
  ];
80
119
  }
120
+ registerOrderbook(marketId, orderbookKey) {
121
+ let activeKeys = this.orderbookKeysByMarketId.get(marketId);
122
+ if (activeKeys === undefined) {
123
+ activeKeys = new Set();
124
+ this.orderbookKeysByMarketId.set(marketId, activeKeys);
125
+ }
126
+ activeKeys.add(orderbookKey);
127
+ }
128
+ releaseOrderbook(marketId, orderbookKey) {
129
+ const activeKeys = this.orderbookKeysByMarketId.get(marketId);
130
+ if (activeKeys === undefined) {
131
+ return;
132
+ }
133
+ activeKeys.delete(orderbookKey);
134
+ if (activeKeys.size > 0) {
135
+ return;
136
+ }
137
+ this.orderbookKeysByMarketId.delete(marketId);
138
+ }
81
139
  onClose() {
82
140
  this.perpCtxBySubDex.clear();
141
+ this.spotCtxByMarketId = {};
83
142
  this.fastCtxByMarketId = {};
143
+ this.orderbookKeysByMarketId.clear();
144
+ this.latestOrderbookByMarketId.clear();
145
+ this.fastDecodeChain = Promise.resolve();
146
+ this.pacDecodeChain = Promise.resolve();
147
+ this.sacDecodeChain = Promise.resolve();
148
+ this.orderbookDecodeChain = Promise.resolve();
84
149
  this.orderUpdatesKey = undefined;
85
150
  }
86
151
  sendSubscribe(payload) {
@@ -90,10 +155,14 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
90
155
  switch (sub.channel) {
91
156
  case 'marketsContext':
92
157
  return 'marketsContext';
158
+ case 'marketContext':
159
+ return `marketContext:${sub.marketId}`;
93
160
  case 'orderbook':
94
- return `l2Book:${sub.marketId}`;
161
+ return `l2:${sub.marketId}:${this.orderbookAggregationKey(sub.marketId, sub.priceStep)}`;
95
162
  case 'candle':
96
163
  return `candle:${sub.marketId}:${sub.interval}`;
164
+ case 'trades':
165
+ return `trades:${sub.marketId}`;
97
166
  case 'orderUpdates':
98
167
  return `orderUpdates:${sub.address.toLowerCase()}`;
99
168
  case 'fills':
@@ -104,24 +173,50 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
104
173
  return `spotState:${sub.address.toLowerCase()}`;
105
174
  }
106
175
  }
176
+ orderbookAggregation(marketId, priceStep) {
177
+ if (priceStep === undefined) {
178
+ return {};
179
+ }
180
+ return (0, index_js_1.priceStepToAggregation)(priceStep, this.orderbookReferencePrice(marketId));
181
+ }
182
+ orderbookAggregationKey(marketId, priceStep) {
183
+ const aggregation = this.orderbookAggregation(marketId, priceStep);
184
+ const nSigFigs = aggregation.nSigFigs ?? null;
185
+ const mantissa = aggregation.mantissa ?? null;
186
+ if (nSigFigs === null && mantissa === null) {
187
+ return 'full';
188
+ }
189
+ return `s:${nSigFigs}:m:${mantissa}`;
190
+ }
107
191
  toHlPayload(sub) {
108
192
  switch (sub.channel) {
109
193
  case 'marketsContext':
110
194
  return { type: 'allDexsAssetCtxs' };
111
- case 'orderbook':
195
+ case 'marketContext':
112
196
  return {
113
- type: 'l2Book',
197
+ type: 'activeAssetCtx',
114
198
  coin: sub.marketId,
115
- ...(sub.priceStep !== undefined
116
- ? (0, index_js_1.priceStepToAggregation)(sub.priceStep, this.mergedMids().get(sub.marketId) ?? Number.NaN)
117
- : {}),
118
199
  };
200
+ case 'orderbook': {
201
+ const aggregation = this.orderbookAggregation(sub.marketId, sub.priceStep);
202
+ return {
203
+ type: 'l2',
204
+ c: sub.marketId,
205
+ s: aggregation.nSigFigs ?? null,
206
+ m: aggregation.mantissa ?? null,
207
+ };
208
+ }
119
209
  case 'candle':
120
210
  return {
121
211
  type: 'candle',
122
212
  coin: sub.marketId,
123
213
  interval: sub.interval,
124
214
  };
215
+ case 'trades':
216
+ return {
217
+ type: 'trades',
218
+ coin: sub.marketId,
219
+ };
125
220
  case 'orderUpdates':
126
221
  return { type: 'orderUpdates', user: sub.address };
127
222
  case 'fills':
@@ -159,15 +254,33 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
159
254
  case 'allDexsAssetCtxs':
160
255
  this.handleAllDexsAssetCtxs(msg.data);
161
256
  break;
257
+ case 'pac':
258
+ this.handlePac(msg.data);
259
+ break;
260
+ case 'sac':
261
+ this.handleSac(msg.data);
262
+ break;
162
263
  case 'fastAssetCtxs':
163
264
  this.handleFastAssetCtxs(msg.data);
164
265
  break;
266
+ case 'activeAssetCtx':
267
+ this.handleActiveAssetCtx(msg.data, raw);
268
+ break;
269
+ case 'activeSpotAssetCtx':
270
+ this.handleActiveSpotAssetCtx(msg.data);
271
+ break;
272
+ case 'l2':
273
+ this.handleL2(msg.data);
274
+ break;
165
275
  case 'l2Book':
166
276
  this.handleL2Book(msg.data);
167
277
  break;
168
278
  case 'candle':
169
279
  this.handleCandle(msg.data);
170
280
  break;
281
+ case 'trades':
282
+ this.handleTrades(msg.data);
283
+ break;
171
284
  case 'orderUpdates':
172
285
  this.handleOrderUpdates(msg.data);
173
286
  break;
@@ -187,14 +300,65 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
187
300
  }
188
301
  }
189
302
  handleAllDexsAssetCtxs(data) {
190
- for (const [dex, ctxs] of data.assetCtxs) {
191
- const byMarketId = {};
303
+ this.mergePerpAssetCtxEntries(allDexsAssetCtxEntries(data));
304
+ this.emitMarketsContext();
305
+ }
306
+ handlePac(base64) {
307
+ this.pacDecodeChain = this.pacDecodeChain
308
+ .then(async () => {
309
+ this.mergePerpAssetCtxEntries(await (0, index_js_1.decodeCompressedJson)(base64));
310
+ this.emitMarketsContext();
311
+ })
312
+ .catch((error) => perps_sdk_1.wsLog.handlerFailure(this.providerKey, error));
313
+ }
314
+ mergePerpAssetCtxEntries(entries) {
315
+ for (const [dex, ctxs] of entries) {
316
+ const previous = this.perpCtxBySubDex.get(dex || 'default');
317
+ const byMarketId = {
318
+ ...(previous ?? {}),
319
+ };
192
320
  for (const ctx of ctxs) {
193
- byMarketId[ctx.coin] = ctx;
321
+ const marketId = ctx.coin;
322
+ if (marketId === undefined) {
323
+ continue;
324
+ }
325
+ const merged = mergePerpAssetCtx(previous?.[marketId], marketId, ctx);
326
+ if (merged !== undefined) {
327
+ byMarketId[marketId] = merged;
328
+ }
194
329
  }
195
330
  this.perpCtxBySubDex.set(dex || 'default', byMarketId);
196
331
  }
197
- this.emitMarketsContext();
332
+ }
333
+ handleSac(base64) {
334
+ this.sacDecodeChain = this.sacDecodeChain
335
+ .then(async () => {
336
+ const spotMarketIdBySacKey = this.spotMarketIdBySacKey();
337
+ const ctxs = await (0, index_js_1.decodeCompressedJson)(base64);
338
+ for (const [sacKey, ctx] of Object.entries(ctxs)) {
339
+ const marketId = spotMarketIdBySacKey.get(sacKey);
340
+ if (marketId === undefined) {
341
+ continue;
342
+ }
343
+ this.spotCtxByMarketId[marketId] = {
344
+ ...this.spotCtxByMarketId[marketId],
345
+ ...ctx,
346
+ };
347
+ }
348
+ this.emitMarketsContext();
349
+ })
350
+ .catch((error) => perps_sdk_1.wsLog.handlerFailure(this.providerKey, error));
351
+ }
352
+ spotMarketIdBySacKey() {
353
+ const map = new Map();
354
+ for (const market of this.registry?.markets ?? []) {
355
+ if (market.categoryId !== constants_js_1.SPOT_MARKET_ID) {
356
+ continue;
357
+ }
358
+ map.set(market.id, market.id);
359
+ map.set(`${market.baseAsset.displaySymbol}/${market.quoteAsset.displaySymbol}`, market.id);
360
+ }
361
+ return map;
198
362
  }
199
363
  handleFastAssetCtxs(base64) {
200
364
  this.fastDecodeChain = this.fastDecodeChain
@@ -224,6 +388,13 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
224
388
  };
225
389
  }
226
390
  }
391
+ for (const [marketId, ctx] of Object.entries(this.spotCtxByMarketId)) {
392
+ const context = mapSpotMarketContext(marketId, ctx, this.fastCtxByMarketId[marketId]);
393
+ if (context === undefined) {
394
+ continue;
395
+ }
396
+ data[marketId] = context;
397
+ }
227
398
  for (const [marketId, fast] of Object.entries(this.fastCtxByMarketId)) {
228
399
  if (data[marketId] !== undefined) {
229
400
  continue;
@@ -236,6 +407,27 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
236
407
  }
237
408
  this.emit('marketsContext', { channel: 'marketsContext', data });
238
409
  }
410
+ handleActiveAssetCtx(data, raw) {
411
+ const ctx = activePerpAssetCtx(data.coin, data.ctx);
412
+ if (ctx === undefined) {
413
+ perps_sdk_1.wsLog.parseFailure(this.providerKey, raw);
414
+ return;
415
+ }
416
+ this.emit(`marketContext:${data.coin}`, {
417
+ channel: 'marketContext',
418
+ data: (0, index_js_1.mapMarketContext)(data.coin, ctx),
419
+ });
420
+ }
421
+ handleActiveSpotAssetCtx(data) {
422
+ const context = mapSpotMarketContext(data.coin, data.ctx);
423
+ if (context === undefined) {
424
+ return;
425
+ }
426
+ this.emit(`marketContext:${data.coin}`, {
427
+ channel: 'marketContext',
428
+ data: context,
429
+ });
430
+ }
239
431
  mergedMids() {
240
432
  const map = new Map();
241
433
  for (const byMarketId of this.perpCtxBySubDex.values()) {
@@ -244,6 +436,12 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
244
436
  map.set(id, Number(mid));
245
437
  }
246
438
  }
439
+ for (const [id, ctx] of Object.entries(this.spotCtxByMarketId)) {
440
+ const mid = ctx.midPx ?? ctx.markPx;
441
+ if (mid != null) {
442
+ map.set(id, Number(mid));
443
+ }
444
+ }
247
445
  for (const [id, fast] of Object.entries(this.fastCtxByMarketId)) {
248
446
  const mid = fast.midPx ?? fast.markPx;
249
447
  if (mid != null) {
@@ -252,21 +450,74 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
252
450
  }
253
451
  return map;
254
452
  }
453
+ orderbookReferencePrice(marketId) {
454
+ const mid = this.mergedMids().get(marketId);
455
+ if (mid !== undefined) {
456
+ return mid;
457
+ }
458
+ const book = this.latestOrderbookByMarketId.get(marketId);
459
+ const bid = book?.bids[0]?.price;
460
+ const ask = book?.asks[0]?.price;
461
+ if (bid !== undefined && ask !== undefined) {
462
+ return (Number(bid) + Number(ask)) / 2;
463
+ }
464
+ return Number.NaN;
465
+ }
255
466
  handleL2Book(data) {
256
- this.emit(`l2Book:${data.coin}`, {
257
- channel: 'orderbook',
258
- data: {
259
- provider: this.providerKey,
260
- marketId: data.coin,
261
- bids: data.levels[0]
262
- .slice(0, HL_L2_BOOK_MAX_LEVELS_PER_SIDE)
263
- .map((l) => ({ price: l.px, size: l.sz })),
264
- asks: data.levels[1]
265
- .slice(0, HL_L2_BOOK_MAX_LEVELS_PER_SIDE)
266
- .map((l) => ({ price: l.px, size: l.sz })),
267
- timestamp: data.time,
268
- },
269
- });
467
+ const book = {
468
+ provider: this.providerKey,
469
+ marketId: data.coin,
470
+ bids: data.levels[0]
471
+ .slice(0, HL_L2_BOOK_MAX_LEVELS_PER_SIDE)
472
+ .map((l) => ({ price: l.px, size: l.sz })),
473
+ asks: data.levels[1]
474
+ .slice(0, HL_L2_BOOK_MAX_LEVELS_PER_SIDE)
475
+ .map((l) => ({ price: l.px, size: l.sz })),
476
+ timestamp: data.time,
477
+ };
478
+ this.latestOrderbookByMarketId.set(data.coin, book);
479
+ this.emitOrderbook(data.coin, book);
480
+ }
481
+ handleL2(data) {
482
+ if (data.s !== undefined) {
483
+ this.handleL2Book(data.s);
484
+ }
485
+ if (data.u !== undefined) {
486
+ this.handleCompactL2Delta(data.u);
487
+ }
488
+ if (data.c === undefined) {
489
+ return;
490
+ }
491
+ const compressed = data.c;
492
+ this.orderbookDecodeChain = this.orderbookDecodeChain
493
+ .then(async () => {
494
+ this.handleCompactL2Delta(await (0, index_js_1.decodeCompressedJson)(compressed));
495
+ })
496
+ .catch((error) => perps_sdk_1.wsLog.handlerFailure(this.providerKey, error));
497
+ }
498
+ handleCompactL2Delta(delta) {
499
+ const previous = this.latestOrderbookByMarketId.get(delta.c);
500
+ if (previous === undefined) {
501
+ return;
502
+ }
503
+ const book = {
504
+ provider: this.providerKey,
505
+ marketId: delta.c,
506
+ bids: applyCompressedL2Side(previous.bids, delta.l[0], delta.r?.[0] ?? [], 'bid'),
507
+ asks: applyCompressedL2Side(previous.asks, delta.l[1], delta.r?.[1] ?? [], 'ask'),
508
+ timestamp: delta.t,
509
+ };
510
+ this.latestOrderbookByMarketId.set(delta.c, book);
511
+ this.emitOrderbook(delta.c, book);
512
+ }
513
+ emitOrderbook(marketId, book) {
514
+ const keys = this.orderbookKeysByMarketId.get(marketId);
515
+ if (keys === undefined) {
516
+ return;
517
+ }
518
+ for (const key of keys) {
519
+ this.emit(key, { channel: 'orderbook', data: book });
520
+ }
270
521
  }
271
522
  handleCandle(data) {
272
523
  this.emit(`candle:${data.s}:${data.i}`, {
@@ -281,6 +532,24 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
281
532
  },
282
533
  });
283
534
  }
535
+ handleTrades(data) {
536
+ for (const trade of data) {
537
+ this.emit(`trades:${trade.coin}`, {
538
+ channel: 'trades',
539
+ data: [
540
+ {
541
+ provider: this.providerKey,
542
+ marketId: trade.coin,
543
+ price: trade.px,
544
+ size: trade.sz,
545
+ timestamp: trade.time,
546
+ side: trade.side === 'B' ? 'buy' : 'sell',
547
+ id: trade.tid !== undefined ? String(trade.tid) : trade.hash,
548
+ },
549
+ ],
550
+ });
551
+ }
552
+ }
284
553
  handleOrderUpdates(data) {
285
554
  const key = this.orderUpdatesKey;
286
555
  if (key === undefined) {
@@ -373,8 +642,134 @@ class HyperliquidWsProvider extends perps_sdk_1.WsProviderBase {
373
642
  }
374
643
  exports.HyperliquidWsProvider = HyperliquidWsProvider;
375
644
  const isObject = (v) => typeof v === 'object' && v !== null;
645
+ const toMarketContextString = (value) => typeof value === 'string' || typeof value === 'number'
646
+ ? String(value)
647
+ : undefined;
648
+ const toMarketCapString = (price, circulatingSupply) => {
649
+ const priceString = toMarketContextString(price);
650
+ const supplyString = toMarketContextString(circulatingSupply);
651
+ if (priceString === undefined || supplyString === undefined) {
652
+ return undefined;
653
+ }
654
+ try {
655
+ const parsedPrice = new big_js_1.default(priceString);
656
+ const parsedSupply = new big_js_1.default(supplyString);
657
+ if (parsedPrice.lte(0) || parsedSupply.lte(0)) {
658
+ return undefined;
659
+ }
660
+ return parsedPrice.times(parsedSupply).toFixed();
661
+ }
662
+ catch {
663
+ return undefined;
664
+ }
665
+ };
666
+ function mergePerpAssetCtx(previous, marketId, update) {
667
+ const funding = update.funding ?? previous?.funding;
668
+ const openInterest = update.openInterest ?? previous?.openInterest;
669
+ const dayNtlVlm = update.dayNtlVlm ?? previous?.dayNtlVlm;
670
+ const prevDayPx = update.prevDayPx ?? previous?.prevDayPx;
671
+ const markPx = update.markPx ?? previous?.markPx;
672
+ const midPx = 'midPx' in update ? update.midPx : previous?.midPx;
673
+ const oraclePx = update.oraclePx ?? previous?.oraclePx;
674
+ if (funding === undefined ||
675
+ openInterest === undefined ||
676
+ dayNtlVlm === undefined ||
677
+ prevDayPx === undefined ||
678
+ markPx === undefined ||
679
+ midPx === undefined ||
680
+ oraclePx === undefined) {
681
+ return undefined;
682
+ }
683
+ return {
684
+ coin: marketId,
685
+ funding,
686
+ openInterest,
687
+ dayNtlVlm,
688
+ prevDayPx,
689
+ markPx,
690
+ midPx,
691
+ oraclePx,
692
+ };
693
+ }
694
+ function allDexsAssetCtxEntries(data) {
695
+ return data.assetCtxs ?? data.ctxs ?? [];
696
+ }
697
+ function activePerpAssetCtx(marketId, ctx) {
698
+ const funding = toMarketContextString(ctx.funding);
699
+ const openInterest = toMarketContextString(ctx.openInterest);
700
+ const dayNtlVlm = toMarketContextString(ctx.dayNtlVlm);
701
+ const prevDayPx = toMarketContextString(ctx.prevDayPx);
702
+ const markPx = toMarketContextString(ctx.markPx);
703
+ const midPx = ctx.midPx === undefined || ctx.midPx === null
704
+ ? null
705
+ : (toMarketContextString(ctx.midPx) ?? null);
706
+ const oraclePx = toMarketContextString(ctx.oraclePx);
707
+ if (funding === undefined ||
708
+ openInterest === undefined ||
709
+ dayNtlVlm === undefined ||
710
+ prevDayPx === undefined ||
711
+ markPx === undefined ||
712
+ oraclePx === undefined) {
713
+ return undefined;
714
+ }
715
+ return {
716
+ coin: marketId,
717
+ funding,
718
+ openInterest,
719
+ dayNtlVlm,
720
+ prevDayPx,
721
+ markPx,
722
+ midPx,
723
+ oraclePx,
724
+ };
725
+ }
726
+ function mapSpotMarketContext(marketId, ctx, fast) {
727
+ const midPrice = toMarketContextString(ctx.midPx ?? ctx.markPx);
728
+ const markPrice = toMarketContextString(ctx.markPx);
729
+ if (midPrice === undefined || markPrice === undefined) {
730
+ return undefined;
731
+ }
732
+ const emittedMarkPrice = fast?.markPx != null ? fast.markPx : markPrice;
733
+ return {
734
+ marketId,
735
+ midPrice: fast?.midPx != null ? fast.midPx : midPrice,
736
+ markPrice: emittedMarkPrice,
737
+ prevDayPrice: toMarketContextString(ctx.prevDayPx),
738
+ volume24h: toMarketContextString(ctx.dayNtlVlm),
739
+ marketCap: toMarketCapString(emittedMarkPrice, ctx.circulatingSupply),
740
+ };
741
+ }
742
+ function compactRemovalPrice(levels, value) {
743
+ if (typeof value === 'number') {
744
+ return levels[value]?.price;
745
+ }
746
+ return typeof value === 'string' ? value : value.p;
747
+ }
748
+ function applyCompressedL2Side(previous, updates, removals, side) {
749
+ const byPrice = new Map(previous.map((level) => [level.price, level.size]));
750
+ for (const removal of removals) {
751
+ const price = compactRemovalPrice(previous, removal);
752
+ if (price !== undefined) {
753
+ byPrice.delete(price);
754
+ }
755
+ }
756
+ for (const update of updates) {
757
+ if (Number(update.s) === 0) {
758
+ byPrice.delete(update.p);
759
+ }
760
+ else {
761
+ byPrice.set(update.p, update.s);
762
+ }
763
+ }
764
+ return [...byPrice.entries()]
765
+ .map(([price, size]) => ({ price, size }))
766
+ .sort((a, b) => side === 'bid'
767
+ ? Number(b.price) - Number(a.price)
768
+ : Number(a.price) - Number(b.price))
769
+ .slice(0, HL_L2_BOOK_MAX_LEVELS_PER_SIDE);
770
+ }
376
771
  function isValidHlFrame(channel, data) {
377
- if (channel === 'fastAssetCtxs') {
772
+ if (channel === 'fastAssetCtxs' || channel === 'pac' || channel === 'sac') {
378
773
  return typeof data === 'string';
379
774
  }
380
775
  if (!isObject(data)) {
@@ -382,15 +777,22 @@ function isValidHlFrame(channel, data) {
382
777
  }
383
778
  switch (channel) {
384
779
  case 'allDexsAssetCtxs':
385
- return Array.isArray(data.assetCtxs);
780
+ return Array.isArray(data.assetCtxs) || Array.isArray(data.ctxs);
781
+ case 'activeAssetCtx':
782
+ case 'activeSpotAssetCtx':
783
+ return typeof data.coin === 'string' && isObject(data.ctx);
386
784
  case 'l2Book':
387
785
  return (typeof data.coin === 'string' &&
388
786
  Array.isArray(data.levels) &&
389
787
  Array.isArray(data.levels[0]) &&
390
788
  Array.isArray(data.levels[1]) &&
391
789
  typeof data.time === 'number');
790
+ case 'l2':
791
+ return typeof data.c === 'string' || isObject(data.s) || isObject(data.u);
392
792
  case 'candle':
393
793
  return typeof data.s === 'string' && typeof data.i === 'string';
794
+ case 'trades':
795
+ return Array.isArray(data);
394
796
  case 'orderUpdates':
395
797
  return Array.isArray(data);
396
798
  case 'userFills':