@metamask-previews/perps-controller 3.1.0-preview-d6985be → 3.1.1-preview-9c8c0a0

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 (40) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/dist/PerpsController.cjs +5 -1
  3. package/dist/PerpsController.cjs.map +1 -1
  4. package/dist/PerpsController.d.cts.map +1 -1
  5. package/dist/PerpsController.d.mts.map +1 -1
  6. package/dist/PerpsController.mjs +5 -1
  7. package/dist/PerpsController.mjs.map +1 -1
  8. package/dist/providers/HyperLiquidProvider.cjs +63 -166
  9. package/dist/providers/HyperLiquidProvider.cjs.map +1 -1
  10. package/dist/providers/HyperLiquidProvider.d.cts.map +1 -1
  11. package/dist/providers/HyperLiquidProvider.d.mts.map +1 -1
  12. package/dist/providers/HyperLiquidProvider.mjs +64 -167
  13. package/dist/providers/HyperLiquidProvider.mjs.map +1 -1
  14. package/dist/services/DexDiscoveryCacheManager.cjs +147 -0
  15. package/dist/services/DexDiscoveryCacheManager.cjs.map +1 -0
  16. package/dist/services/DexDiscoveryCacheManager.d.cts +52 -0
  17. package/dist/services/DexDiscoveryCacheManager.d.cts.map +1 -0
  18. package/dist/services/DexDiscoveryCacheManager.d.mts +52 -0
  19. package/dist/services/DexDiscoveryCacheManager.d.mts.map +1 -0
  20. package/dist/services/DexDiscoveryCacheManager.mjs +143 -0
  21. package/dist/services/DexDiscoveryCacheManager.mjs.map +1 -0
  22. package/dist/services/HyperLiquidClientService.cjs +4 -3
  23. package/dist/services/HyperLiquidClientService.cjs.map +1 -1
  24. package/dist/services/HyperLiquidClientService.d.cts.map +1 -1
  25. package/dist/services/HyperLiquidClientService.d.mts.map +1 -1
  26. package/dist/services/HyperLiquidClientService.mjs +4 -3
  27. package/dist/services/HyperLiquidClientService.mjs.map +1 -1
  28. package/dist/services/HyperLiquidSubscriptionService.cjs +34 -2
  29. package/dist/services/HyperLiquidSubscriptionService.cjs.map +1 -1
  30. package/dist/services/HyperLiquidSubscriptionService.d.cts.map +1 -1
  31. package/dist/services/HyperLiquidSubscriptionService.d.mts.map +1 -1
  32. package/dist/services/HyperLiquidSubscriptionService.mjs +34 -2
  33. package/dist/services/HyperLiquidSubscriptionService.mjs.map +1 -1
  34. package/dist/types/perps-types.cjs.map +1 -1
  35. package/dist/types/perps-types.d.cts +13 -0
  36. package/dist/types/perps-types.d.cts.map +1 -1
  37. package/dist/types/perps-types.d.mts +13 -0
  38. package/dist/types/perps-types.d.mts.map +1 -1
  39. package/dist/types/perps-types.mjs.map +1 -1
  40. package/package.json +4 -4
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _DexDiscoveryCacheManager_deps;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.DexDiscoveryCacheManager = void 0;
16
+ const hyperLiquidConfig_1 = require("../constants/hyperLiquidConfig.cjs");
17
+ /**
18
+ * Manages the unified DEX discovery cache — single source of truth for all perpDexs() derivatives.
19
+ *
20
+ * Extracted from HyperLiquidProvider to isolate cache logic.
21
+ * All writes go through update(); readers use .state.
22
+ */
23
+ class DexDiscoveryCacheManager {
24
+ constructor(deps) {
25
+ /**
26
+ * Unified DEX discovery state.
27
+ * null = not yet fetched; object = raw + validated + timestamp.
28
+ */
29
+ this.state = null;
30
+ _DexDiscoveryCacheManager_deps.set(this, void 0);
31
+ __classPrivateFieldSet(this, _DexDiscoveryCacheManager_deps, deps, "f");
32
+ }
33
+ /**
34
+ * Single atomic writer for DEX discovery state.
35
+ * All code paths that fetch perpDexs() MUST call this — no direct field writes.
36
+ *
37
+ * @param allDexs - Raw perpDexs() API response array.
38
+ * @returns The newly created unified discovery state.
39
+ */
40
+ update(allDexs) {
41
+ const validated = this.computeValidatedDexs(allDexs);
42
+ const newState = {
43
+ raw: allDexs,
44
+ validated,
45
+ timestamp: Date.now(),
46
+ };
47
+ this.state = newState;
48
+ return newState;
49
+ }
50
+ /**
51
+ * Reset state to null (used on disconnect/reconnect).
52
+ */
53
+ reset() {
54
+ this.state = null;
55
+ }
56
+ /**
57
+ * Pure filtering of perpDexs() response into validated DEX names.
58
+ * Encapsulates testnet/mainnet feature-flag logic.
59
+ *
60
+ * @param allDexs - Raw perpDexs() API response array.
61
+ * @returns Filtered DEX name list (null = main DEX, strings = HIP-3 DEXs).
62
+ */
63
+ computeValidatedDexs(allDexs) {
64
+ const availableHip3Dexs = [];
65
+ allDexs.forEach((dex) => {
66
+ if (dex !== null) {
67
+ availableHip3Dexs.push(dex.name);
68
+ }
69
+ });
70
+ if (__classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").isTestnetMode()) {
71
+ const { EnabledDexs, AutoDiscoverAll } = hyperLiquidConfig_1.TESTNET_HIP3_CONFIG;
72
+ if (!AutoDiscoverAll) {
73
+ if (EnabledDexs.length === 0) {
74
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Testnet - using main DEX only (HIP-3 DEXs filtered)', {
75
+ availableHip3Dexs: availableHip3Dexs.length,
76
+ reason: 'TESTNET_HIP3_CONFIG.EnabledDexs is empty',
77
+ });
78
+ return [null];
79
+ }
80
+ const filteredDexs = availableHip3Dexs.filter((dex) => EnabledDexs.includes(dex));
81
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Testnet - filtered to allowed DEXs', {
82
+ allowedDexs: EnabledDexs,
83
+ filteredDexs,
84
+ availableHip3Dexs: availableHip3Dexs.length,
85
+ });
86
+ return [null, ...filteredDexs];
87
+ }
88
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Testnet - AUTO_DISCOVER_ALL enabled, using all DEXs', { totalDexCount: availableHip3Dexs.length + 1 });
89
+ }
90
+ else {
91
+ const { AutoDiscoverAll } = hyperLiquidConfig_1.MAINNET_HIP3_CONFIG;
92
+ if (!AutoDiscoverAll) {
93
+ const allowedDexsFromAllowlist = this.extractDexsFromAllowlist();
94
+ if (allowedDexsFromAllowlist.length === 0) {
95
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Mainnet - using main DEX only (no HIP-3 DEXs in allowlist)', {
96
+ availableHip3Dexs: availableHip3Dexs.length,
97
+ allowlistMarkets: __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").getAllowlistMarkets(),
98
+ });
99
+ return [null];
100
+ }
101
+ const filteredDexs = availableHip3Dexs.filter((dex) => allowedDexsFromAllowlist.includes(dex));
102
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Mainnet - filtered to allowlist DEXs', {
103
+ allowedDexsFromAllowlist,
104
+ filteredDexs,
105
+ availableHip3Dexs: availableHip3Dexs.length,
106
+ });
107
+ return [null, ...filteredDexs];
108
+ }
109
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Mainnet - AUTO_DISCOVER_ALL enabled, using all DEXs', { totalDexCount: availableHip3Dexs.length + 1 });
110
+ }
111
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: All DEXs enabled (market filtering at data layer)', {
112
+ mainDex: true,
113
+ hip3Dexs: availableHip3Dexs,
114
+ totalDexCount: availableHip3Dexs.length + 1,
115
+ });
116
+ return [null, ...availableHip3Dexs];
117
+ }
118
+ /**
119
+ * Extract unique DEX names from allowlist market patterns.
120
+ * Patterns can be: "xyz:*" (wildcard), "xyz:TSLA" (exact), or "xyz" (DEX shorthand).
121
+ *
122
+ * @returns Array of unique DEX names from the allowlist.
123
+ */
124
+ extractDexsFromAllowlist() {
125
+ const allowlistMarkets = __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").getAllowlistMarkets();
126
+ if (allowlistMarkets.length === 0) {
127
+ return [];
128
+ }
129
+ const dexNames = new Set();
130
+ for (const pattern of allowlistMarkets) {
131
+ const colonIndex = pattern.indexOf(':');
132
+ if (colonIndex > 0) {
133
+ const dex = pattern.substring(0, colonIndex);
134
+ dexNames.add(dex);
135
+ }
136
+ else if (pattern.length > 0 && !pattern.includes('*')) {
137
+ if (/^[a-z][a-z0-9]*$/iu.test(pattern)) {
138
+ dexNames.add(pattern.toLowerCase());
139
+ }
140
+ }
141
+ }
142
+ return Array.from(dexNames);
143
+ }
144
+ }
145
+ exports.DexDiscoveryCacheManager = DexDiscoveryCacheManager;
146
+ _DexDiscoveryCacheManager_deps = new WeakMap();
147
+ //# sourceMappingURL=DexDiscoveryCacheManager.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DexDiscoveryCacheManager.cjs","sourceRoot":"","sources":["../../src/services/DexDiscoveryCacheManager.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n MAINNET_HIP3_CONFIG,\n TESTNET_HIP3_CONFIG,\n} from '../constants/hyperLiquidConfig';\nimport type { DexDiscoveryState, ExtendedPerpDex } from '../types/perps-types';\n\ntype DexDiscoveryDeps = {\n isTestnetMode: () => boolean;\n debugLogger: { log: (...args: unknown[]) => void };\n getAllowlistMarkets: () => string[];\n};\n\n/**\n * Manages the unified DEX discovery cache — single source of truth for all perpDexs() derivatives.\n *\n * Extracted from HyperLiquidProvider to isolate cache logic.\n * All writes go through update(); readers use .state.\n */\nexport class DexDiscoveryCacheManager {\n /**\n * Unified DEX discovery state.\n * null = not yet fetched; object = raw + validated + timestamp.\n */\n state: DexDiscoveryState | null = null;\n\n readonly #deps: DexDiscoveryDeps;\n\n constructor(deps: DexDiscoveryDeps) {\n this.#deps = deps;\n }\n\n /**\n * Single atomic writer for DEX discovery state.\n * All code paths that fetch perpDexs() MUST call this — no direct field writes.\n *\n * @param allDexs - Raw perpDexs() API response array.\n * @returns The newly created unified discovery state.\n */\n update(allDexs: (ExtendedPerpDex | null)[]): DexDiscoveryState {\n const validated = this.computeValidatedDexs(allDexs);\n const newState: DexDiscoveryState = {\n raw: allDexs,\n validated,\n timestamp: Date.now(),\n };\n this.state = newState;\n return newState;\n }\n\n /**\n * Reset state to null (used on disconnect/reconnect).\n */\n reset(): void {\n this.state = null;\n }\n\n /**\n * Pure filtering of perpDexs() response into validated DEX names.\n * Encapsulates testnet/mainnet feature-flag logic.\n *\n * @param allDexs - Raw perpDexs() API response array.\n * @returns Filtered DEX name list (null = main DEX, strings = HIP-3 DEXs).\n */\n computeValidatedDexs(allDexs: (ExtendedPerpDex | null)[]): (string | null)[] {\n const availableHip3Dexs: string[] = [];\n allDexs.forEach((dex) => {\n if (dex !== null) {\n availableHip3Dexs.push(dex.name);\n }\n });\n\n if (this.#deps.isTestnetMode()) {\n const { EnabledDexs, AutoDiscoverAll } = TESTNET_HIP3_CONFIG;\n\n if (!AutoDiscoverAll) {\n if (EnabledDexs.length === 0) {\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Testnet - using main DEX only (HIP-3 DEXs filtered)',\n {\n availableHip3Dexs: availableHip3Dexs.length,\n reason: 'TESTNET_HIP3_CONFIG.EnabledDexs is empty',\n },\n );\n return [null];\n }\n\n const filteredDexs = availableHip3Dexs.filter((dex) =>\n EnabledDexs.includes(dex),\n );\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Testnet - filtered to allowed DEXs',\n {\n allowedDexs: EnabledDexs,\n filteredDexs,\n availableHip3Dexs: availableHip3Dexs.length,\n },\n );\n return [null, ...filteredDexs];\n }\n\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Testnet - AUTO_DISCOVER_ALL enabled, using all DEXs',\n { totalDexCount: availableHip3Dexs.length + 1 },\n );\n } else {\n const { AutoDiscoverAll } = MAINNET_HIP3_CONFIG;\n\n if (!AutoDiscoverAll) {\n const allowedDexsFromAllowlist = this.extractDexsFromAllowlist();\n\n if (allowedDexsFromAllowlist.length === 0) {\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Mainnet - using main DEX only (no HIP-3 DEXs in allowlist)',\n {\n availableHip3Dexs: availableHip3Dexs.length,\n allowlistMarkets: this.#deps.getAllowlistMarkets(),\n },\n );\n return [null];\n }\n\n const filteredDexs = availableHip3Dexs.filter((dex) =>\n allowedDexsFromAllowlist.includes(dex),\n );\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Mainnet - filtered to allowlist DEXs',\n {\n allowedDexsFromAllowlist,\n filteredDexs,\n availableHip3Dexs: availableHip3Dexs.length,\n },\n );\n return [null, ...filteredDexs];\n }\n\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Mainnet - AUTO_DISCOVER_ALL enabled, using all DEXs',\n { totalDexCount: availableHip3Dexs.length + 1 },\n );\n }\n\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: All DEXs enabled (market filtering at data layer)',\n {\n mainDex: true,\n hip3Dexs: availableHip3Dexs,\n totalDexCount: availableHip3Dexs.length + 1,\n },\n );\n return [null, ...availableHip3Dexs];\n }\n\n /**\n * Extract unique DEX names from allowlist market patterns.\n * Patterns can be: \"xyz:*\" (wildcard), \"xyz:TSLA\" (exact), or \"xyz\" (DEX shorthand).\n *\n * @returns Array of unique DEX names from the allowlist.\n */\n extractDexsFromAllowlist(): string[] {\n const allowlistMarkets = this.#deps.getAllowlistMarkets();\n if (allowlistMarkets.length === 0) {\n return [];\n }\n\n const dexNames = new Set<string>();\n\n for (const pattern of allowlistMarkets) {\n const colonIndex = pattern.indexOf(':');\n if (colonIndex > 0) {\n const dex = pattern.substring(0, colonIndex);\n dexNames.add(dex);\n } else if (pattern.length > 0 && !pattern.includes('*')) {\n if (/^[a-z][a-z0-9]*$/iu.test(pattern)) {\n dexNames.add(pattern.toLowerCase());\n }\n }\n }\n\n return Array.from(dexNames);\n }\n}\n"]}
@@ -0,0 +1,52 @@
1
+ import type { DexDiscoveryState, ExtendedPerpDex } from "../types/perps-types.cjs";
2
+ type DexDiscoveryDeps = {
3
+ isTestnetMode: () => boolean;
4
+ debugLogger: {
5
+ log: (...args: unknown[]) => void;
6
+ };
7
+ getAllowlistMarkets: () => string[];
8
+ };
9
+ /**
10
+ * Manages the unified DEX discovery cache — single source of truth for all perpDexs() derivatives.
11
+ *
12
+ * Extracted from HyperLiquidProvider to isolate cache logic.
13
+ * All writes go through update(); readers use .state.
14
+ */
15
+ export declare class DexDiscoveryCacheManager {
16
+ #private;
17
+ /**
18
+ * Unified DEX discovery state.
19
+ * null = not yet fetched; object = raw + validated + timestamp.
20
+ */
21
+ state: DexDiscoveryState | null;
22
+ constructor(deps: DexDiscoveryDeps);
23
+ /**
24
+ * Single atomic writer for DEX discovery state.
25
+ * All code paths that fetch perpDexs() MUST call this — no direct field writes.
26
+ *
27
+ * @param allDexs - Raw perpDexs() API response array.
28
+ * @returns The newly created unified discovery state.
29
+ */
30
+ update(allDexs: (ExtendedPerpDex | null)[]): DexDiscoveryState;
31
+ /**
32
+ * Reset state to null (used on disconnect/reconnect).
33
+ */
34
+ reset(): void;
35
+ /**
36
+ * Pure filtering of perpDexs() response into validated DEX names.
37
+ * Encapsulates testnet/mainnet feature-flag logic.
38
+ *
39
+ * @param allDexs - Raw perpDexs() API response array.
40
+ * @returns Filtered DEX name list (null = main DEX, strings = HIP-3 DEXs).
41
+ */
42
+ computeValidatedDexs(allDexs: (ExtendedPerpDex | null)[]): (string | null)[];
43
+ /**
44
+ * Extract unique DEX names from allowlist market patterns.
45
+ * Patterns can be: "xyz:*" (wildcard), "xyz:TSLA" (exact), or "xyz" (DEX shorthand).
46
+ *
47
+ * @returns Array of unique DEX names from the allowlist.
48
+ */
49
+ extractDexsFromAllowlist(): string[];
50
+ }
51
+ export {};
52
+ //# sourceMappingURL=DexDiscoveryCacheManager.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DexDiscoveryCacheManager.d.cts","sourceRoot":"","sources":["../../src/services/DexDiscoveryCacheManager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,iCAA6B;AAE/E,KAAK,gBAAgB,GAAG;IACtB,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,WAAW,EAAE;QAAE,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;KAAE,CAAC;IACnD,mBAAmB,EAAE,MAAM,MAAM,EAAE,CAAC;CACrC,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,wBAAwB;;IACnC;;;OAGG;IACH,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAQ;gBAI3B,IAAI,EAAE,gBAAgB;IAIlC;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,EAAE,CAAC,eAAe,GAAG,IAAI,CAAC,EAAE,GAAG,iBAAiB;IAW9D;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;;;;;OAMG;IACH,oBAAoB,CAAC,OAAO,EAAE,CAAC,eAAe,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE;IAyF5E;;;;;OAKG;IACH,wBAAwB,IAAI,MAAM,EAAE;CAsBrC"}
@@ -0,0 +1,52 @@
1
+ import type { DexDiscoveryState, ExtendedPerpDex } from "../types/perps-types.mjs";
2
+ type DexDiscoveryDeps = {
3
+ isTestnetMode: () => boolean;
4
+ debugLogger: {
5
+ log: (...args: unknown[]) => void;
6
+ };
7
+ getAllowlistMarkets: () => string[];
8
+ };
9
+ /**
10
+ * Manages the unified DEX discovery cache — single source of truth for all perpDexs() derivatives.
11
+ *
12
+ * Extracted from HyperLiquidProvider to isolate cache logic.
13
+ * All writes go through update(); readers use .state.
14
+ */
15
+ export declare class DexDiscoveryCacheManager {
16
+ #private;
17
+ /**
18
+ * Unified DEX discovery state.
19
+ * null = not yet fetched; object = raw + validated + timestamp.
20
+ */
21
+ state: DexDiscoveryState | null;
22
+ constructor(deps: DexDiscoveryDeps);
23
+ /**
24
+ * Single atomic writer for DEX discovery state.
25
+ * All code paths that fetch perpDexs() MUST call this — no direct field writes.
26
+ *
27
+ * @param allDexs - Raw perpDexs() API response array.
28
+ * @returns The newly created unified discovery state.
29
+ */
30
+ update(allDexs: (ExtendedPerpDex | null)[]): DexDiscoveryState;
31
+ /**
32
+ * Reset state to null (used on disconnect/reconnect).
33
+ */
34
+ reset(): void;
35
+ /**
36
+ * Pure filtering of perpDexs() response into validated DEX names.
37
+ * Encapsulates testnet/mainnet feature-flag logic.
38
+ *
39
+ * @param allDexs - Raw perpDexs() API response array.
40
+ * @returns Filtered DEX name list (null = main DEX, strings = HIP-3 DEXs).
41
+ */
42
+ computeValidatedDexs(allDexs: (ExtendedPerpDex | null)[]): (string | null)[];
43
+ /**
44
+ * Extract unique DEX names from allowlist market patterns.
45
+ * Patterns can be: "xyz:*" (wildcard), "xyz:TSLA" (exact), or "xyz" (DEX shorthand).
46
+ *
47
+ * @returns Array of unique DEX names from the allowlist.
48
+ */
49
+ extractDexsFromAllowlist(): string[];
50
+ }
51
+ export {};
52
+ //# sourceMappingURL=DexDiscoveryCacheManager.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DexDiscoveryCacheManager.d.mts","sourceRoot":"","sources":["../../src/services/DexDiscoveryCacheManager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,iCAA6B;AAE/E,KAAK,gBAAgB,GAAG;IACtB,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,WAAW,EAAE;QAAE,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;KAAE,CAAC;IACnD,mBAAmB,EAAE,MAAM,MAAM,EAAE,CAAC;CACrC,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,wBAAwB;;IACnC;;;OAGG;IACH,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAQ;gBAI3B,IAAI,EAAE,gBAAgB;IAIlC;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,EAAE,CAAC,eAAe,GAAG,IAAI,CAAC,EAAE,GAAG,iBAAiB;IAW9D;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;;;;;OAMG;IACH,oBAAoB,CAAC,OAAO,EAAE,CAAC,eAAe,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE;IAyF5E;;;;;OAKG;IACH,wBAAwB,IAAI,MAAM,EAAE;CAsBrC"}
@@ -0,0 +1,143 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _DexDiscoveryCacheManager_deps;
13
+ import { MAINNET_HIP3_CONFIG, TESTNET_HIP3_CONFIG } from "../constants/hyperLiquidConfig.mjs";
14
+ /**
15
+ * Manages the unified DEX discovery cache — single source of truth for all perpDexs() derivatives.
16
+ *
17
+ * Extracted from HyperLiquidProvider to isolate cache logic.
18
+ * All writes go through update(); readers use .state.
19
+ */
20
+ export class DexDiscoveryCacheManager {
21
+ constructor(deps) {
22
+ /**
23
+ * Unified DEX discovery state.
24
+ * null = not yet fetched; object = raw + validated + timestamp.
25
+ */
26
+ this.state = null;
27
+ _DexDiscoveryCacheManager_deps.set(this, void 0);
28
+ __classPrivateFieldSet(this, _DexDiscoveryCacheManager_deps, deps, "f");
29
+ }
30
+ /**
31
+ * Single atomic writer for DEX discovery state.
32
+ * All code paths that fetch perpDexs() MUST call this — no direct field writes.
33
+ *
34
+ * @param allDexs - Raw perpDexs() API response array.
35
+ * @returns The newly created unified discovery state.
36
+ */
37
+ update(allDexs) {
38
+ const validated = this.computeValidatedDexs(allDexs);
39
+ const newState = {
40
+ raw: allDexs,
41
+ validated,
42
+ timestamp: Date.now(),
43
+ };
44
+ this.state = newState;
45
+ return newState;
46
+ }
47
+ /**
48
+ * Reset state to null (used on disconnect/reconnect).
49
+ */
50
+ reset() {
51
+ this.state = null;
52
+ }
53
+ /**
54
+ * Pure filtering of perpDexs() response into validated DEX names.
55
+ * Encapsulates testnet/mainnet feature-flag logic.
56
+ *
57
+ * @param allDexs - Raw perpDexs() API response array.
58
+ * @returns Filtered DEX name list (null = main DEX, strings = HIP-3 DEXs).
59
+ */
60
+ computeValidatedDexs(allDexs) {
61
+ const availableHip3Dexs = [];
62
+ allDexs.forEach((dex) => {
63
+ if (dex !== null) {
64
+ availableHip3Dexs.push(dex.name);
65
+ }
66
+ });
67
+ if (__classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").isTestnetMode()) {
68
+ const { EnabledDexs, AutoDiscoverAll } = TESTNET_HIP3_CONFIG;
69
+ if (!AutoDiscoverAll) {
70
+ if (EnabledDexs.length === 0) {
71
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Testnet - using main DEX only (HIP-3 DEXs filtered)', {
72
+ availableHip3Dexs: availableHip3Dexs.length,
73
+ reason: 'TESTNET_HIP3_CONFIG.EnabledDexs is empty',
74
+ });
75
+ return [null];
76
+ }
77
+ const filteredDexs = availableHip3Dexs.filter((dex) => EnabledDexs.includes(dex));
78
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Testnet - filtered to allowed DEXs', {
79
+ allowedDexs: EnabledDexs,
80
+ filteredDexs,
81
+ availableHip3Dexs: availableHip3Dexs.length,
82
+ });
83
+ return [null, ...filteredDexs];
84
+ }
85
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Testnet - AUTO_DISCOVER_ALL enabled, using all DEXs', { totalDexCount: availableHip3Dexs.length + 1 });
86
+ }
87
+ else {
88
+ const { AutoDiscoverAll } = MAINNET_HIP3_CONFIG;
89
+ if (!AutoDiscoverAll) {
90
+ const allowedDexsFromAllowlist = this.extractDexsFromAllowlist();
91
+ if (allowedDexsFromAllowlist.length === 0) {
92
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Mainnet - using main DEX only (no HIP-3 DEXs in allowlist)', {
93
+ availableHip3Dexs: availableHip3Dexs.length,
94
+ allowlistMarkets: __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").getAllowlistMarkets(),
95
+ });
96
+ return [null];
97
+ }
98
+ const filteredDexs = availableHip3Dexs.filter((dex) => allowedDexsFromAllowlist.includes(dex));
99
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Mainnet - filtered to allowlist DEXs', {
100
+ allowedDexsFromAllowlist,
101
+ filteredDexs,
102
+ availableHip3Dexs: availableHip3Dexs.length,
103
+ });
104
+ return [null, ...filteredDexs];
105
+ }
106
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: Mainnet - AUTO_DISCOVER_ALL enabled, using all DEXs', { totalDexCount: availableHip3Dexs.length + 1 });
107
+ }
108
+ __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").debugLogger.log('HyperLiquidProvider: All DEXs enabled (market filtering at data layer)', {
109
+ mainDex: true,
110
+ hip3Dexs: availableHip3Dexs,
111
+ totalDexCount: availableHip3Dexs.length + 1,
112
+ });
113
+ return [null, ...availableHip3Dexs];
114
+ }
115
+ /**
116
+ * Extract unique DEX names from allowlist market patterns.
117
+ * Patterns can be: "xyz:*" (wildcard), "xyz:TSLA" (exact), or "xyz" (DEX shorthand).
118
+ *
119
+ * @returns Array of unique DEX names from the allowlist.
120
+ */
121
+ extractDexsFromAllowlist() {
122
+ const allowlistMarkets = __classPrivateFieldGet(this, _DexDiscoveryCacheManager_deps, "f").getAllowlistMarkets();
123
+ if (allowlistMarkets.length === 0) {
124
+ return [];
125
+ }
126
+ const dexNames = new Set();
127
+ for (const pattern of allowlistMarkets) {
128
+ const colonIndex = pattern.indexOf(':');
129
+ if (colonIndex > 0) {
130
+ const dex = pattern.substring(0, colonIndex);
131
+ dexNames.add(dex);
132
+ }
133
+ else if (pattern.length > 0 && !pattern.includes('*')) {
134
+ if (/^[a-z][a-z0-9]*$/iu.test(pattern)) {
135
+ dexNames.add(pattern.toLowerCase());
136
+ }
137
+ }
138
+ }
139
+ return Array.from(dexNames);
140
+ }
141
+ }
142
+ _DexDiscoveryCacheManager_deps = new WeakMap();
143
+ //# sourceMappingURL=DexDiscoveryCacheManager.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DexDiscoveryCacheManager.mjs","sourceRoot":"","sources":["../../src/services/DexDiscoveryCacheManager.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n MAINNET_HIP3_CONFIG,\n TESTNET_HIP3_CONFIG,\n} from '../constants/hyperLiquidConfig';\nimport type { DexDiscoveryState, ExtendedPerpDex } from '../types/perps-types';\n\ntype DexDiscoveryDeps = {\n isTestnetMode: () => boolean;\n debugLogger: { log: (...args: unknown[]) => void };\n getAllowlistMarkets: () => string[];\n};\n\n/**\n * Manages the unified DEX discovery cache — single source of truth for all perpDexs() derivatives.\n *\n * Extracted from HyperLiquidProvider to isolate cache logic.\n * All writes go through update(); readers use .state.\n */\nexport class DexDiscoveryCacheManager {\n /**\n * Unified DEX discovery state.\n * null = not yet fetched; object = raw + validated + timestamp.\n */\n state: DexDiscoveryState | null = null;\n\n readonly #deps: DexDiscoveryDeps;\n\n constructor(deps: DexDiscoveryDeps) {\n this.#deps = deps;\n }\n\n /**\n * Single atomic writer for DEX discovery state.\n * All code paths that fetch perpDexs() MUST call this — no direct field writes.\n *\n * @param allDexs - Raw perpDexs() API response array.\n * @returns The newly created unified discovery state.\n */\n update(allDexs: (ExtendedPerpDex | null)[]): DexDiscoveryState {\n const validated = this.computeValidatedDexs(allDexs);\n const newState: DexDiscoveryState = {\n raw: allDexs,\n validated,\n timestamp: Date.now(),\n };\n this.state = newState;\n return newState;\n }\n\n /**\n * Reset state to null (used on disconnect/reconnect).\n */\n reset(): void {\n this.state = null;\n }\n\n /**\n * Pure filtering of perpDexs() response into validated DEX names.\n * Encapsulates testnet/mainnet feature-flag logic.\n *\n * @param allDexs - Raw perpDexs() API response array.\n * @returns Filtered DEX name list (null = main DEX, strings = HIP-3 DEXs).\n */\n computeValidatedDexs(allDexs: (ExtendedPerpDex | null)[]): (string | null)[] {\n const availableHip3Dexs: string[] = [];\n allDexs.forEach((dex) => {\n if (dex !== null) {\n availableHip3Dexs.push(dex.name);\n }\n });\n\n if (this.#deps.isTestnetMode()) {\n const { EnabledDexs, AutoDiscoverAll } = TESTNET_HIP3_CONFIG;\n\n if (!AutoDiscoverAll) {\n if (EnabledDexs.length === 0) {\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Testnet - using main DEX only (HIP-3 DEXs filtered)',\n {\n availableHip3Dexs: availableHip3Dexs.length,\n reason: 'TESTNET_HIP3_CONFIG.EnabledDexs is empty',\n },\n );\n return [null];\n }\n\n const filteredDexs = availableHip3Dexs.filter((dex) =>\n EnabledDexs.includes(dex),\n );\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Testnet - filtered to allowed DEXs',\n {\n allowedDexs: EnabledDexs,\n filteredDexs,\n availableHip3Dexs: availableHip3Dexs.length,\n },\n );\n return [null, ...filteredDexs];\n }\n\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Testnet - AUTO_DISCOVER_ALL enabled, using all DEXs',\n { totalDexCount: availableHip3Dexs.length + 1 },\n );\n } else {\n const { AutoDiscoverAll } = MAINNET_HIP3_CONFIG;\n\n if (!AutoDiscoverAll) {\n const allowedDexsFromAllowlist = this.extractDexsFromAllowlist();\n\n if (allowedDexsFromAllowlist.length === 0) {\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Mainnet - using main DEX only (no HIP-3 DEXs in allowlist)',\n {\n availableHip3Dexs: availableHip3Dexs.length,\n allowlistMarkets: this.#deps.getAllowlistMarkets(),\n },\n );\n return [null];\n }\n\n const filteredDexs = availableHip3Dexs.filter((dex) =>\n allowedDexsFromAllowlist.includes(dex),\n );\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Mainnet - filtered to allowlist DEXs',\n {\n allowedDexsFromAllowlist,\n filteredDexs,\n availableHip3Dexs: availableHip3Dexs.length,\n },\n );\n return [null, ...filteredDexs];\n }\n\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: Mainnet - AUTO_DISCOVER_ALL enabled, using all DEXs',\n { totalDexCount: availableHip3Dexs.length + 1 },\n );\n }\n\n this.#deps.debugLogger.log(\n 'HyperLiquidProvider: All DEXs enabled (market filtering at data layer)',\n {\n mainDex: true,\n hip3Dexs: availableHip3Dexs,\n totalDexCount: availableHip3Dexs.length + 1,\n },\n );\n return [null, ...availableHip3Dexs];\n }\n\n /**\n * Extract unique DEX names from allowlist market patterns.\n * Patterns can be: \"xyz:*\" (wildcard), \"xyz:TSLA\" (exact), or \"xyz\" (DEX shorthand).\n *\n * @returns Array of unique DEX names from the allowlist.\n */\n extractDexsFromAllowlist(): string[] {\n const allowlistMarkets = this.#deps.getAllowlistMarkets();\n if (allowlistMarkets.length === 0) {\n return [];\n }\n\n const dexNames = new Set<string>();\n\n for (const pattern of allowlistMarkets) {\n const colonIndex = pattern.indexOf(':');\n if (colonIndex > 0) {\n const dex = pattern.substring(0, colonIndex);\n dexNames.add(dex);\n } else if (pattern.length > 0 && !pattern.includes('*')) {\n if (/^[a-z][a-z0-9]*$/iu.test(pattern)) {\n dexNames.add(pattern.toLowerCase());\n }\n }\n }\n\n return Array.from(dexNames);\n }\n}\n"]}
@@ -315,9 +315,10 @@ class HyperLiquidClientService {
315
315
  const now = endTime ?? Date.now();
316
316
  const intervalMs = __classPrivateFieldGet(this, _HyperLiquidClientService_instances, "m", _HyperLiquidClientService_getIntervalMilliseconds).call(this, interval);
317
317
  const startTime = now - limit * intervalMs;
318
- // Use the SDK's InfoClient to fetch candle data
319
- // HyperLiquid SDK uses 'coin' terminology
320
- const infoClient = this.getInfoClient();
318
+ // Use HTTP transport for historical candle snapshots (request/response).
319
+ // This avoids the WebSocket abort race condition that causes 429s
320
+ // during rapid market switching on extension (#TAT-2954).
321
+ const infoClient = this.getInfoClient({ useHttp: true });
321
322
  const data = await infoClient.candleSnapshot({
322
323
  coin: symbol, // Map to HyperLiquid SDK's 'coin' parameter
323
324
  interval,