@lifi/perps-sdk-provider-hyperliquid 1.5.4 → 1.5.6
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/cjs/services/getFills.d.ts.map +1 -1
- package/dist/cjs/services/getFills.js +13 -6
- package/dist/cjs/services/getFills.js.map +1 -1
- package/dist/cjs/services/getOrders.js +1 -1
- package/dist/cjs/services/getOrders.js.map +1 -1
- package/dist/cjs/types/order.d.ts +1 -1
- package/dist/cjs/types/order.d.ts.map +1 -1
- package/dist/cjs/utils/decodeFastAssetCtxs.d.ts.map +1 -1
- package/dist/cjs/utils/decodeFastAssetCtxs.js +11 -6
- package/dist/cjs/utils/decodeFastAssetCtxs.js.map +1 -1
- package/dist/cjs/websocket/HyperliquidWsProvider.d.ts +7 -4
- package/dist/cjs/websocket/HyperliquidWsProvider.d.ts.map +1 -1
- package/dist/cjs/websocket/HyperliquidWsProvider.js +77 -62
- package/dist/cjs/websocket/HyperliquidWsProvider.js.map +1 -1
- package/dist/cjs/websocket/decodeChain.d.ts +11 -0
- package/dist/cjs/websocket/decodeChain.d.ts.map +1 -0
- package/dist/cjs/websocket/decodeChain.js +37 -0
- package/dist/cjs/websocket/decodeChain.js.map +1 -0
- package/dist/esm/services/getFills.d.ts +4 -4
- package/dist/esm/services/getFills.d.ts.map +1 -1
- package/dist/esm/services/getFills.js +19 -9
- package/dist/esm/services/getFills.js.map +1 -1
- package/dist/esm/services/getOrders.js +1 -1
- package/dist/esm/services/getOrders.js.map +1 -1
- package/dist/esm/types/order.d.ts +1 -1
- package/dist/esm/types/order.d.ts.map +1 -1
- package/dist/esm/utils/decodeFastAssetCtxs.d.ts +1 -0
- package/dist/esm/utils/decodeFastAssetCtxs.d.ts.map +1 -1
- package/dist/esm/utils/decodeFastAssetCtxs.js +14 -6
- package/dist/esm/utils/decodeFastAssetCtxs.js.map +1 -1
- package/dist/esm/websocket/HyperliquidWsProvider.d.ts +20 -7
- package/dist/esm/websocket/HyperliquidWsProvider.d.ts.map +1 -1
- package/dist/esm/websocket/HyperliquidWsProvider.js +100 -71
- package/dist/esm/websocket/HyperliquidWsProvider.js.map +1 -1
- package/dist/esm/websocket/decodeChain.d.ts +30 -0
- package/dist/esm/websocket/decodeChain.d.ts.map +1 -0
- package/dist/esm/websocket/decodeChain.js +44 -0
- package/dist/esm/websocket/decodeChain.js.map +1 -0
- package/dist/types/services/getFills.d.ts +4 -4
- package/dist/types/services/getFills.d.ts.map +1 -1
- package/dist/types/types/order.d.ts +1 -1
- package/dist/types/types/order.d.ts.map +1 -1
- package/dist/types/utils/decodeFastAssetCtxs.d.ts +1 -0
- package/dist/types/utils/decodeFastAssetCtxs.d.ts.map +1 -1
- package/dist/types/websocket/HyperliquidWsProvider.d.ts +20 -7
- package/dist/types/websocket/HyperliquidWsProvider.d.ts.map +1 -1
- package/dist/types/websocket/decodeChain.d.ts +30 -0
- package/dist/types/websocket/decodeChain.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/services/getFills.ts +37 -11
- package/src/services/getOrders.ts +1 -1
- package/src/types/order.ts +1 -1
- package/src/utils/decodeFastAssetCtxs.ts +14 -6
- package/src/websocket/HyperliquidWsProvider.ts +136 -93
- package/src/websocket/decodeChain.ts +54 -0
|
@@ -10,7 +10,7 @@ import type { HyperliquidContext } from '../context.js';
|
|
|
10
10
|
export interface GetFillsParams {
|
|
11
11
|
address: Address;
|
|
12
12
|
limit?: number;
|
|
13
|
-
/**
|
|
13
|
+
/** Opaque pagination cursor from a previous page's `pagination.cursor`. */
|
|
14
14
|
cursor?: string;
|
|
15
15
|
/** Inclusive lower bound in ms-since-epoch — switches to the `userFillsByTime` endpoint. */
|
|
16
16
|
startTime?: number;
|
|
@@ -24,9 +24,9 @@ export interface GetFillsParams {
|
|
|
24
24
|
* list supplies the display fields; only the fills endpoint is read direct
|
|
25
25
|
* from HL.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* next
|
|
27
|
+
* The response is sorted (time desc, `tid` as tiebreaker) before pagination —
|
|
28
|
+
* see {@link compareFillsDesc}. The cursor encodes the last returned fill's
|
|
29
|
+
* `(time, tid)`; the next page keeps fills that sort strictly after it.
|
|
30
30
|
* @throws {PerpsError} On Hyperliquid REST error, network, or parsing failures.
|
|
31
31
|
* @public
|
|
32
32
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFills.d.ts","sourceRoot":"","sources":["../../../src/services/getFills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAMnC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"getFills.d.ts","sourceRoot":"","sources":["../../../src/services/getFills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAMnC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AASvD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAoBD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ,GACnB,oBAAoB,kBAAkB,EACtC,QAAQ,cAAc,EACtB,UAAU,iBAAiB,KAC1B,OAAO,CAAC,aAAa,CAqDvB,CAAA"}
|
|
@@ -2,6 +2,15 @@ import { getMarketRegistry } from '@lifi/perps-sdk';
|
|
|
2
2
|
import { DEFAULT_HISTORY_LIMIT, MAX_HISTORY_LIMIT, PROVIDER_KEY, } from '../constants.js';
|
|
3
3
|
import { mapFill } from '../utils/index.js';
|
|
4
4
|
import { hlInfoOptions, infoRequest } from '../utils/infoClient.js';
|
|
5
|
+
// HL's docs specify neither newest-first ordering nor monotonic `tid` for
|
|
6
|
+
// `userFills`/`userFillsByTime`, so pagination can't rely on upstream order —
|
|
7
|
+
// it must impose its own (time desc, tid desc as tiebreaker for same-time fills).
|
|
8
|
+
const compareFillsDesc = (a, b) => b.time - a.time || b.tid - a.tid;
|
|
9
|
+
const encodeCursor = (fill) => `${fill.time}:${fill.tid}`;
|
|
10
|
+
const decodeCursor = (cursor) => {
|
|
11
|
+
const [time, tid] = cursor.split(':').map(Number);
|
|
12
|
+
return { time, tid };
|
|
13
|
+
};
|
|
5
14
|
/**
|
|
6
15
|
* Fetch the user's fills history. When `startTime` or `endTime` is provided,
|
|
7
16
|
* Hyperliquid's `userFillsByTime` endpoint is used; otherwise the unbounded
|
|
@@ -9,9 +18,9 @@ import { hlInfoOptions, infoRequest } from '../utils/infoClient.js';
|
|
|
9
18
|
* list supplies the display fields; only the fills endpoint is read direct
|
|
10
19
|
* from HL.
|
|
11
20
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* next
|
|
21
|
+
* The response is sorted (time desc, `tid` as tiebreaker) before pagination —
|
|
22
|
+
* see {@link compareFillsDesc}. The cursor encodes the last returned fill's
|
|
23
|
+
* `(time, tid)`; the next page keeps fills that sort strictly after it.
|
|
15
24
|
* @throws {PerpsError} On Hyperliquid REST error, network, or parsing failures.
|
|
16
25
|
* @public
|
|
17
26
|
*/
|
|
@@ -29,20 +38,21 @@ export const getFills = async ({ client, apiUrl }, params, options) => {
|
|
|
29
38
|
...(params.endTime !== undefined ? { endTime: params.endTime } : {}),
|
|
30
39
|
}, infoOpts)
|
|
31
40
|
: await infoRequest(apiUrl, { type: 'userFills', user: params.address }, infoOpts);
|
|
41
|
+
const sorted = [...allFills].sort(compareFillsDesc);
|
|
32
42
|
const filtered = params.cursor === undefined
|
|
33
|
-
?
|
|
34
|
-
:
|
|
43
|
+
? sorted
|
|
44
|
+
: sorted.filter((f) => compareFillsDesc(f, decodeCursor(params.cursor)) > 0);
|
|
35
45
|
const hasMore = filtered.length > limit;
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
const page = filtered.slice(0, limit);
|
|
47
|
+
const items = page.map((f) => mapFill(f, registry.require(f.coin)));
|
|
48
|
+
const lastPageFill = page[page.length - 1];
|
|
39
49
|
return {
|
|
40
50
|
provider: PROVIDER_KEY,
|
|
41
51
|
items,
|
|
42
52
|
pagination: {
|
|
43
53
|
limit,
|
|
44
54
|
hasMore,
|
|
45
|
-
cursor:
|
|
55
|
+
cursor: lastPageFill ? encodeCursor(lastPageFill) : undefined,
|
|
46
56
|
},
|
|
47
57
|
};
|
|
48
58
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFills.js","sourceRoot":"","sources":["../../../src/services/getFills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA0B,MAAM,iBAAiB,CAAA;AAG3E,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,GACb,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"getFills.js","sourceRoot":"","sources":["../../../src/services/getFills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA0B,MAAM,iBAAiB,CAAA;AAG3E,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,GACb,MAAM,iBAAiB,CAAA;AAOxB,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAuBnE,0EAA0E;AAC1E,8EAA8E;AAC9E,kFAAkF;AAClF,MAAM,gBAAgB,GAAG,CAAC,CAAa,EAAE,CAAa,EAAU,EAAE,CAChE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAA;AAElC,MAAM,YAAY,GAAG,CAAC,IAAgB,EAAU,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,CAAA;AAE7E,MAAM,YAAY,GAAG,CAAC,MAAc,EAAc,EAAE;IAClD,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACjD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAA;AACtB,CAAC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,EAC3B,EAAE,MAAM,EAAE,MAAM,EAAsB,EACtC,MAAsB,EACtB,OAA2B,EACH,EAAE;IAC1B,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACxD,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;IACrB,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAE/C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,MAAM,CAAC,KAAK,IAAI,qBAAqB,EACrC,iBAAiB,CAClB,CAAA;IAED,MAAM,SAAS,GACb,MAAM,CAAC,SAAS,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,CAAA;IAEhE,MAAM,QAAQ,GAAG,SAAS;QACxB,CAAC,CAAC,MAAM,WAAW,CACf,MAAM,EACN;YACE,IAAI,EAAE,iBAAiB;YACvB,IAAI,EAAE,MAAM,CAAC,OAAO;YACpB,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,CAAC;YAChC,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACrE,EACD,QAAQ,CACT;QACH,CAAC,CAAC,MAAM,WAAW,CACf,MAAM,EACN,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,EAC3C,QAAQ,CACT,CAAA;IAEL,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;IAEnD,MAAM,QAAQ,GACZ,MAAM,CAAC,MAAM,KAAK,SAAS;QACzB,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,MAAM,CAAC,MAAM,CACX,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,MAAO,CAAC,CAAC,GAAG,CAAC,CAC7D,CAAA;IAEP,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAA;IACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IACrC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACnE,MAAM,YAAY,GAA2B,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAElE,OAAO;QACL,QAAQ,EAAE,YAAY;QACtB,KAAK;QACL,UAAU,EAAE;YACV,KAAK;YACL,OAAO;YACP,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS;SAC9D;KACF,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -24,7 +24,7 @@ export const getOrders = async ({ client, apiUrl }, params, options) => {
|
|
|
24
24
|
const raw = ordersResults.flat();
|
|
25
25
|
const childOids = new Set();
|
|
26
26
|
for (const o of raw) {
|
|
27
|
-
for (const child of o.children) {
|
|
27
|
+
for (const child of o.children ?? []) {
|
|
28
28
|
childOids.add(child.oid);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getOrders.js","sourceRoot":"","sources":["../../../src/services/getOrders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA0B,MAAM,iBAAiB,CAAA;AAG3E,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAM9C,OAAO,EACL,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,GACd,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAcnE;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAC5B,EAAE,MAAM,EAAE,MAAM,EAAsB,EACtC,MAAuB,EACvB,OAA2B,EACF,EAAE;IAC3B,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACxD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;IACrC,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAE/C,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CACrC,aAAa,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAClC,WAAW,CACT,MAAM,EACN;QACE,IAAI,EAAE,oBAAoB;QAC1B,IAAI,EAAE,MAAM,CAAC,OAAO;QACpB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/B,EACD,QAAQ,CACT,CACF,CACF,CAAA;IAED,MAAM,GAAG,GAA0B,aAAa,CAAC,IAAI,EAAE,CAAA;IAEvD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAA;IACnC,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;QACpB,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"getOrders.js","sourceRoot":"","sources":["../../../src/services/getOrders.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"order.d.ts","sourceRoot":"","sources":["../../../src/types/order.ts"],"names":[],"mappings":"AAEA,cAAc;AACd,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,cAAc,EAAE,OAAO,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,mBAAmB,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"order.d.ts","sourceRoot":"","sources":["../../../src/types/order.ts"],"names":[],"mappings":"AAEA,cAAc;AACd,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,cAAc,EAAE,OAAO,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAChC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,EAAE,CAAA;AAExD,cAAc;AACd,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAA;QACX,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,EAAE,EAAE,MAAM,CAAA;QACV,OAAO,EAAE,MAAM,CAAA;QACf,SAAS,EAAE,MAAM,CAAA;QACjB,MAAM,EAAE,MAAM,CAAA;QACd,UAAU,EAAE,OAAO,CAAA;QACnB,SAAS,EAAE,MAAM,CAAA;QACjB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;QAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;QACpB,gBAAgB,EAAE,MAAM,CAAA;QACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;KACzB,CAAA;IACD,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;CACrB,CAAA;AAED,cAAc;AACd,MAAM,MAAM,qBAAqB,GAC7B,kBAAkB,GAClB;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,CAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { HlWsFastAssetCtx } from '../types/index.js';
|
|
2
|
+
/** Decode a base64 + raw-DEFLATE (RFC 1951) payload into parsed JSON. */
|
|
2
3
|
export declare function decodeCompressedJson<T>(base64: string): Promise<T>;
|
|
3
4
|
/**
|
|
4
5
|
* Decode a Hyperliquid `fastAssetCtxs` frame. HL sends the payload as a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decodeFastAssetCtxs.d.ts","sourceRoot":"","sources":["../../../src/utils/decodeFastAssetCtxs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEzD,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"decodeFastAssetCtxs.d.ts","sourceRoot":"","sources":["../../../src/utils/decodeFastAssetCtxs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEzD,yEAAyE;AACzE,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAcxE;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAE3C"}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
/** Decode a base64 + raw-DEFLATE (RFC 1951) payload into parsed JSON. */
|
|
1
2
|
export async function decodeCompressedJson(base64) {
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
.
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const binary = atob(base64);
|
|
4
|
+
const bytes = new Uint8Array(binary.length);
|
|
5
|
+
for (let i = 0; i < binary.length; i++) {
|
|
6
|
+
bytes[i] = binary.charCodeAt(i);
|
|
7
|
+
}
|
|
8
|
+
const stream = new DecompressionStream('deflate-raw');
|
|
9
|
+
const text = new Response(stream.readable).text();
|
|
10
|
+
const writer = stream.writable.getWriter();
|
|
11
|
+
// Write-side failures (bad DEFLATE) also error the readable and reject
|
|
12
|
+
// `text`; detach these rejections to avoid unhandled duplicates.
|
|
13
|
+
writer.write(bytes).catch(() => undefined);
|
|
14
|
+
writer.close().catch(() => undefined);
|
|
15
|
+
return JSON.parse(await text);
|
|
8
16
|
}
|
|
9
17
|
/**
|
|
10
18
|
* Decode a Hyperliquid `fastAssetCtxs` frame. HL sends the payload as a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decodeFastAssetCtxs.js","sourceRoot":"","sources":["../../../src/utils/decodeFastAssetCtxs.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAI,MAAc;IAC1D,MAAM,KAAK,GAAG,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"decodeFastAssetCtxs.js","sourceRoot":"","sources":["../../../src/utils/decodeFastAssetCtxs.ts"],"names":[],"mappings":"AAEA,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAI,MAAc;IAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAA;IAC3B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;IACjC,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,mBAAmB,CAAC,aAAa,CAAC,CAAA;IACrD,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAA;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAA;IAC1C,uEAAuE;IACvE,iEAAiE;IACjE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAC1C,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IACrC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAM,CAAA;AACpC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAc;IAEd,OAAO,oBAAoB,CAAmC,MAAM,CAAC,CAAA;AACvE,CAAC"}
|
|
@@ -30,13 +30,15 @@ export declare class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
30
30
|
private readonly registry;
|
|
31
31
|
private perpCtxBySubDex;
|
|
32
32
|
private spotCtxByMarketId;
|
|
33
|
+
private marketsContextByMarketId;
|
|
33
34
|
private orderbookKeysByMarketId;
|
|
34
35
|
private latestOrderbookByMarketId;
|
|
35
36
|
private fastCtxByMarketId;
|
|
36
|
-
private
|
|
37
|
-
private
|
|
38
|
-
private
|
|
39
|
-
private
|
|
37
|
+
private readonly reportDecodeFailure;
|
|
38
|
+
private readonly fastDecodeChain;
|
|
39
|
+
private readonly pacDecodeChain;
|
|
40
|
+
private readonly sacDecodeChain;
|
|
41
|
+
private readonly orderbookDecodeChain;
|
|
40
42
|
constructor(wsUrl: string, providerKey: string, client?: PerpsSDKClient);
|
|
41
43
|
subscribeQuote(params: ProviderGetQuoteParams, onQuote: QuoteListener): Promise<() => void>;
|
|
42
44
|
protected openChannel(sub: Subscription): Promise<() => void>;
|
|
@@ -65,6 +67,7 @@ export declare class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
65
67
|
protected handleMessage(raw: string): void;
|
|
66
68
|
private handleAllDexsAssetCtxs;
|
|
67
69
|
private handlePac;
|
|
70
|
+
/** Merge perp asset contexts in place; returns the marketIds this frame touched. */
|
|
68
71
|
private mergePerpAssetCtxEntries;
|
|
69
72
|
private handleSac;
|
|
70
73
|
private spotMarketIdBySacKey;
|
|
@@ -72,14 +75,24 @@ export declare class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
72
75
|
* Decode a `fastAssetCtxs` frame (base64 + raw-DEFLATE) and merge its changed
|
|
73
76
|
* coins into the per-market fast-context store. Frames are incremental, so a
|
|
74
77
|
* field absent from this frame keeps its prior value. Decodes are chained to
|
|
75
|
-
* preserve arrival order across the async boundary
|
|
78
|
+
* preserve arrival order across the async boundary; a backlog coalesces to
|
|
79
|
+
* its newest frame (see the chain's mode).
|
|
76
80
|
*/
|
|
77
81
|
private handleFastAssetCtxs;
|
|
78
82
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
83
|
+
* Emit the all-markets context map, re-mapping only the markets the incoming
|
|
84
|
+
* frame touched. Each emission is a fresh snapshot object that carries
|
|
85
|
+
* untouched entries over by reference, so a listener holding an earlier
|
|
86
|
+
* snapshot never observes later-frame mutation.
|
|
81
87
|
*/
|
|
82
88
|
private emitMarketsContext;
|
|
89
|
+
/**
|
|
90
|
+
* Context for one market from the cached feeds: spot asset context when it
|
|
91
|
+
* maps, else perp asset context with mid + mark overlaid from
|
|
92
|
+
* `fastAssetCtxs`, else the fast feed alone — each price standing in for the
|
|
93
|
+
* other where it carries only one of mid/mark.
|
|
94
|
+
*/
|
|
95
|
+
private computeMarketContext;
|
|
83
96
|
private handleActiveAssetCtx;
|
|
84
97
|
private handleActiveSpotAssetCtx;
|
|
85
98
|
/** Latest mid per `Market.id` across asset-context and fast feeds. */
|
|
@@ -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,EAOL,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAA;
|
|
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;AA8C1B;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,QAC5B,iBAEgD,CAAA;AAEtD;;;;;;;;;;;;;GAaG;AACH,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,wBAAwB,CAAoC;IACpE,OAAO,CAAC,uBAAuB,CAAiC;IAChE,OAAO,CAAC,yBAAyB,CAAuC;IAKxE,OAAO,CAAC,iBAAiB,CAAuC;IAChE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CACW;IAM/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAG/B;IACD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAG9B;IACD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAG9B;IACD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAGpC;gBAEW,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;IAgEnE;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,iBAAiB;IAmBzB;;;;OAIG;IACH,OAAO,CAAC,2BAA2B;IAWnC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,gBAAgB;cAcL,OAAO,IAAI,IAAI;IAclC,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;IAM9B,OAAO,CAAC,SAAS;IAUjB,oFAAoF;IACpF,OAAO,CAAC,wBAAwB;IAyBhC,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,oBAAoB;IAe5B;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IAc3B;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IA6B5B,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,wBAAwB;IAWhC,sEAAsE;IACtE,OAAO,CAAC,UAAU;IAuBlB,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,QAAQ;IAkBhB,OAAO,CAAC,oBAAoB;IAmC5B,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,kBAAkB;IAyD1B,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,+BAA+B;IAkBvC,OAAO,CAAC,eAAe;CAaxB"}
|
|
@@ -3,6 +3,7 @@ import { OrderSide, OrderType, } from '@lifi/perps-types';
|
|
|
3
3
|
import Big from 'big.js';
|
|
4
4
|
import { HYPERLIQUID_FEE_TIER_FALLBACK, SPOT_MARKET_ID } from '../constants.js';
|
|
5
5
|
import { decodeCompressedJson, decodeFastAssetCtxs, isOpenAssetPosition, isTriggerOrder, mapFill, mapMarketContext, mapOrderStatus, mapOrderType, mapPosition, priceStepToAggregation, spotAssetFromToken, spotBalance, spotPriceById, } from '../utils/index.js';
|
|
6
|
+
import { DecodeChain } from './decodeChain.js';
|
|
6
7
|
/** HL's compact `l2` snapshot carries 20 levels per side. */
|
|
7
8
|
const HL_L2_BOOK_MAX_LEVELS_PER_SIDE = 20;
|
|
8
9
|
/**
|
|
@@ -35,6 +36,7 @@ export class HyperliquidWsProvider extends WsProviderBase {
|
|
|
35
36
|
registry;
|
|
36
37
|
perpCtxBySubDex = new Map();
|
|
37
38
|
spotCtxByMarketId = {};
|
|
39
|
+
marketsContextByMarketId = {};
|
|
38
40
|
orderbookKeysByMarketId = new Map();
|
|
39
41
|
latestOrderbookByMarketId = new Map();
|
|
40
42
|
// Latest mid/mark per `Market.id` from the high-frequency `fastAssetCtxs`
|
|
@@ -42,12 +44,16 @@ export class HyperliquidWsProvider extends WsProviderBase {
|
|
|
42
44
|
// frames carry only changed coins — and overlaid onto asset contexts that
|
|
43
45
|
// carry oracle/funding/OI/metadata.
|
|
44
46
|
fastCtxByMarketId = {};
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
reportDecodeFailure = (error) => wsLog.handlerFailure(this.providerKey, error);
|
|
48
|
+
// Compressed payloads (base64 + raw-DEFLATE) decode async, so each channel
|
|
49
|
+
// chains decodes to apply frames in arrival order. Only the fast feed
|
|
50
|
+
// coalesces a stall backlog to its newest frame: a coin's dropped tick is
|
|
51
|
+
// refreshed by its next one. pac/sac field-merges and l2 deltas depend on
|
|
52
|
+
// every frame, so they must not coalesce.
|
|
53
|
+
fastDecodeChain = new DecodeChain('latest', this.reportDecodeFailure);
|
|
54
|
+
pacDecodeChain = new DecodeChain('every', this.reportDecodeFailure);
|
|
55
|
+
sacDecodeChain = new DecodeChain('every', this.reportDecodeFailure);
|
|
56
|
+
orderbookDecodeChain = new DecodeChain('every', this.reportDecodeFailure);
|
|
51
57
|
constructor(wsUrl, providerKey, client) {
|
|
52
58
|
super(new ReconnectingWebSocket(wsUrl, { pingPayload: '{"method":"ping"}' }), providerKey);
|
|
53
59
|
this.client = client;
|
|
@@ -84,6 +90,7 @@ export class HyperliquidWsProvider extends WsProviderBase {
|
|
|
84
90
|
this.perpCtxBySubDex.clear();
|
|
85
91
|
this.spotCtxByMarketId = {};
|
|
86
92
|
this.fastCtxByMarketId = {};
|
|
93
|
+
this.marketsContextByMarketId = {};
|
|
87
94
|
};
|
|
88
95
|
}
|
|
89
96
|
// All other channels: single WS subscription per key
|
|
@@ -179,12 +186,13 @@ export class HyperliquidWsProvider extends WsProviderBase {
|
|
|
179
186
|
this.perpCtxBySubDex.clear();
|
|
180
187
|
this.spotCtxByMarketId = {};
|
|
181
188
|
this.fastCtxByMarketId = {};
|
|
189
|
+
this.marketsContextByMarketId = {};
|
|
182
190
|
this.orderbookKeysByMarketId.clear();
|
|
183
191
|
this.latestOrderbookByMarketId.clear();
|
|
184
|
-
this.fastDecodeChain
|
|
185
|
-
this.pacDecodeChain
|
|
186
|
-
this.sacDecodeChain
|
|
187
|
-
this.orderbookDecodeChain
|
|
192
|
+
this.fastDecodeChain.reset();
|
|
193
|
+
this.pacDecodeChain.reset();
|
|
194
|
+
this.sacDecodeChain.reset();
|
|
195
|
+
this.orderbookDecodeChain.reset();
|
|
188
196
|
this.orderUpdatesKey = undefined;
|
|
189
197
|
}
|
|
190
198
|
sendSubscribe(payload) {
|
|
@@ -341,41 +349,41 @@ export class HyperliquidWsProvider extends WsProviderBase {
|
|
|
341
349
|
}
|
|
342
350
|
}
|
|
343
351
|
handleAllDexsAssetCtxs(data) {
|
|
344
|
-
this.mergePerpAssetCtxEntries(allDexsAssetCtxEntries(data));
|
|
345
|
-
this.emitMarketsContext();
|
|
352
|
+
this.emitMarketsContext(this.mergePerpAssetCtxEntries(allDexsAssetCtxEntries(data)));
|
|
346
353
|
}
|
|
347
354
|
handlePac(base64) {
|
|
348
|
-
this.pacDecodeChain
|
|
349
|
-
.
|
|
350
|
-
|
|
351
|
-
this.emitMarketsContext();
|
|
352
|
-
})
|
|
353
|
-
.catch((error) => wsLog.handlerFailure(this.providerKey, error));
|
|
355
|
+
this.pacDecodeChain.push(async () => {
|
|
356
|
+
this.emitMarketsContext(this.mergePerpAssetCtxEntries(await decodeCompressedJson(base64)));
|
|
357
|
+
});
|
|
354
358
|
}
|
|
359
|
+
/** Merge perp asset contexts in place; returns the marketIds this frame touched. */
|
|
355
360
|
mergePerpAssetCtxEntries(entries) {
|
|
361
|
+
const touched = new Set();
|
|
356
362
|
for (const [dex, ctxs] of entries) {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
363
|
+
let byMarketId = this.perpCtxBySubDex.get(dex || 'default');
|
|
364
|
+
if (byMarketId === undefined) {
|
|
365
|
+
byMarketId = {};
|
|
366
|
+
this.perpCtxBySubDex.set(dex || 'default', byMarketId);
|
|
367
|
+
}
|
|
361
368
|
for (const ctx of ctxs) {
|
|
362
369
|
const marketId = ctx.coin;
|
|
363
370
|
if (marketId === undefined) {
|
|
364
371
|
continue;
|
|
365
372
|
}
|
|
366
|
-
const merged = mergePerpAssetCtx(
|
|
373
|
+
const merged = mergePerpAssetCtx(byMarketId[marketId], marketId, ctx);
|
|
367
374
|
if (merged !== undefined) {
|
|
368
375
|
byMarketId[marketId] = merged;
|
|
376
|
+
touched.add(marketId);
|
|
369
377
|
}
|
|
370
378
|
}
|
|
371
|
-
this.perpCtxBySubDex.set(dex || 'default', byMarketId);
|
|
372
379
|
}
|
|
380
|
+
return touched;
|
|
373
381
|
}
|
|
374
382
|
handleSac(base64) {
|
|
375
|
-
this.sacDecodeChain
|
|
376
|
-
.then(async () => {
|
|
383
|
+
this.sacDecodeChain.push(async () => {
|
|
377
384
|
const spotMarketIdBySacKey = this.spotMarketIdBySacKey();
|
|
378
385
|
const ctxs = await decodeCompressedJson(base64);
|
|
386
|
+
const touched = new Set();
|
|
379
387
|
for (const [sacKey, ctx] of Object.entries(ctxs)) {
|
|
380
388
|
const marketId = spotMarketIdBySacKey.get(sacKey);
|
|
381
389
|
if (marketId === undefined) {
|
|
@@ -385,10 +393,10 @@ export class HyperliquidWsProvider extends WsProviderBase {
|
|
|
385
393
|
...this.spotCtxByMarketId[marketId],
|
|
386
394
|
...ctx,
|
|
387
395
|
};
|
|
396
|
+
touched.add(marketId);
|
|
388
397
|
}
|
|
389
|
-
this.emitMarketsContext();
|
|
390
|
-
})
|
|
391
|
-
.catch((error) => wsLog.handlerFailure(this.providerKey, error));
|
|
398
|
+
this.emitMarketsContext(touched);
|
|
399
|
+
});
|
|
392
400
|
}
|
|
393
401
|
spotMarketIdBySacKey() {
|
|
394
402
|
const map = new Map();
|
|
@@ -405,11 +413,11 @@ export class HyperliquidWsProvider extends WsProviderBase {
|
|
|
405
413
|
* Decode a `fastAssetCtxs` frame (base64 + raw-DEFLATE) and merge its changed
|
|
406
414
|
* coins into the per-market fast-context store. Frames are incremental, so a
|
|
407
415
|
* field absent from this frame keeps its prior value. Decodes are chained to
|
|
408
|
-
* preserve arrival order across the async boundary
|
|
416
|
+
* preserve arrival order across the async boundary; a backlog coalesces to
|
|
417
|
+
* its newest frame (see the chain's mode).
|
|
409
418
|
*/
|
|
410
419
|
handleFastAssetCtxs(base64) {
|
|
411
|
-
this.fastDecodeChain
|
|
412
|
-
.then(async () => {
|
|
420
|
+
this.fastDecodeChain.push(async () => {
|
|
413
421
|
const ctxs = await decodeFastAssetCtxs(base64);
|
|
414
422
|
for (const [marketId, ctx] of Object.entries(ctxs)) {
|
|
415
423
|
const prev = this.fastCtxByMarketId[marketId];
|
|
@@ -418,48 +426,65 @@ export class HyperliquidWsProvider extends WsProviderBase {
|
|
|
418
426
|
midPx: 'midPx' in ctx ? ctx.midPx : prev?.midPx,
|
|
419
427
|
};
|
|
420
428
|
}
|
|
421
|
-
this.emitMarketsContext();
|
|
422
|
-
})
|
|
423
|
-
.catch((error) => wsLog.handlerFailure(this.providerKey, error));
|
|
429
|
+
this.emitMarketsContext(Object.keys(ctxs));
|
|
430
|
+
});
|
|
424
431
|
}
|
|
425
432
|
/**
|
|
426
|
-
*
|
|
427
|
-
*
|
|
433
|
+
* Emit the all-markets context map, re-mapping only the markets the incoming
|
|
434
|
+
* frame touched. Each emission is a fresh snapshot object that carries
|
|
435
|
+
* untouched entries over by reference, so a listener holding an earlier
|
|
436
|
+
* snapshot never observes later-frame mutation.
|
|
428
437
|
*/
|
|
429
|
-
emitMarketsContext() {
|
|
430
|
-
const data = {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
data[marketId] = {
|
|
436
|
-
...base,
|
|
437
|
-
midPrice: fast?.midPx != null ? fast.midPx : base.midPrice,
|
|
438
|
-
markPrice: fast?.markPx != null ? fast.markPx : base.markPrice,
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
for (const [marketId, ctx] of Object.entries(this.spotCtxByMarketId)) {
|
|
443
|
-
const context = mapSpotMarketContext(marketId, ctx, this.fastCtxByMarketId[marketId]);
|
|
438
|
+
emitMarketsContext(touchedMarketIds) {
|
|
439
|
+
const data = {
|
|
440
|
+
...this.marketsContextByMarketId,
|
|
441
|
+
};
|
|
442
|
+
for (const marketId of touchedMarketIds) {
|
|
443
|
+
const context = this.computeMarketContext(marketId);
|
|
444
444
|
if (context === undefined) {
|
|
445
|
-
|
|
446
|
-
}
|
|
447
|
-
data[marketId] = context;
|
|
448
|
-
}
|
|
449
|
-
for (const [marketId, fast] of Object.entries(this.fastCtxByMarketId)) {
|
|
450
|
-
if (data[marketId] !== undefined) {
|
|
451
|
-
continue;
|
|
445
|
+
delete data[marketId];
|
|
452
446
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const midPrice = fast.midPx ?? fast.markPx;
|
|
456
|
-
const markPrice = fast.markPx ?? fast.midPx;
|
|
457
|
-
if (midPrice != null && markPrice != null) {
|
|
458
|
-
data[marketId] = { marketId, midPrice, markPrice };
|
|
447
|
+
else {
|
|
448
|
+
data[marketId] = context;
|
|
459
449
|
}
|
|
460
450
|
}
|
|
451
|
+
this.marketsContextByMarketId = data;
|
|
461
452
|
this.emit('marketsContext', { channel: 'marketsContext', data });
|
|
462
453
|
}
|
|
454
|
+
/**
|
|
455
|
+
* Context for one market from the cached feeds: spot asset context when it
|
|
456
|
+
* maps, else perp asset context with mid + mark overlaid from
|
|
457
|
+
* `fastAssetCtxs`, else the fast feed alone — each price standing in for the
|
|
458
|
+
* other where it carries only one of mid/mark.
|
|
459
|
+
*/
|
|
460
|
+
computeMarketContext(marketId) {
|
|
461
|
+
const fast = this.fastCtxByMarketId[marketId];
|
|
462
|
+
const spotCtx = this.spotCtxByMarketId[marketId];
|
|
463
|
+
if (spotCtx !== undefined) {
|
|
464
|
+
const context = mapSpotMarketContext(marketId, spotCtx, fast);
|
|
465
|
+
if (context !== undefined) {
|
|
466
|
+
return context;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
let perpCtx;
|
|
470
|
+
for (const byMarketId of this.perpCtxBySubDex.values()) {
|
|
471
|
+
perpCtx = byMarketId[marketId] ?? perpCtx;
|
|
472
|
+
}
|
|
473
|
+
if (perpCtx !== undefined) {
|
|
474
|
+
const base = mapMarketContext(marketId, perpCtx);
|
|
475
|
+
return {
|
|
476
|
+
...base,
|
|
477
|
+
midPrice: fast?.midPx != null ? fast.midPx : base.midPrice,
|
|
478
|
+
markPrice: fast?.markPx != null ? fast.markPx : base.markPrice,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
const midPrice = fast?.midPx ?? fast?.markPx;
|
|
482
|
+
const markPrice = fast?.markPx ?? fast?.midPx;
|
|
483
|
+
if (midPrice != null && markPrice != null) {
|
|
484
|
+
return { marketId, midPrice, markPrice };
|
|
485
|
+
}
|
|
486
|
+
return undefined;
|
|
487
|
+
}
|
|
463
488
|
handleActiveAssetCtx(data, raw) {
|
|
464
489
|
const ctx = activePerpAssetCtx(data.coin, data.ctx);
|
|
465
490
|
if (ctx === undefined) {
|
|
@@ -543,17 +568,22 @@ export class HyperliquidWsProvider extends WsProviderBase {
|
|
|
543
568
|
return;
|
|
544
569
|
}
|
|
545
570
|
const compressed = data.c;
|
|
546
|
-
this.orderbookDecodeChain
|
|
547
|
-
.then(async () => {
|
|
571
|
+
this.orderbookDecodeChain.push(async () => {
|
|
548
572
|
this.handleCompactL2Delta(await decodeCompressedJson(compressed));
|
|
549
|
-
})
|
|
550
|
-
.catch((error) => wsLog.handlerFailure(this.providerKey, error));
|
|
573
|
+
});
|
|
551
574
|
}
|
|
552
575
|
handleCompactL2Delta(delta) {
|
|
553
576
|
const previous = this.latestOrderbookByMarketId.get(delta.c);
|
|
554
577
|
if (previous === undefined) {
|
|
555
578
|
return;
|
|
556
579
|
}
|
|
580
|
+
// Compact deltas decode asynchronously via `orderbookDecodeChain`, so one
|
|
581
|
+
// can resolve after a newer frame (sync snapshot/update, or a later delta)
|
|
582
|
+
// has already been applied. A delta no newer than the maintained book is
|
|
583
|
+
// stale; applying it would overwrite fresher state with older levels.
|
|
584
|
+
if (delta.t <= previous.timestamp) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
557
587
|
const book = {
|
|
558
588
|
provider: this.providerKey,
|
|
559
589
|
marketId: delta.c,
|
|
@@ -621,8 +651,7 @@ export class HyperliquidWsProvider extends WsProviderBase {
|
|
|
621
651
|
continue;
|
|
622
652
|
}
|
|
623
653
|
const type = mapOrderType(o.orderType);
|
|
624
|
-
//
|
|
625
|
-
// (the id may have listed after the snapshot); skip just this item.
|
|
654
|
+
// Unknown market id (absent from the synced snapshot); skip just this item.
|
|
626
655
|
const market = this.registry?.get(o.coin);
|
|
627
656
|
if (!market) {
|
|
628
657
|
continue;
|