@syldel/hl-shared-types 0.0.21 → 0.0.23

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/LICENSE.MD CHANGED
@@ -1,5 +1,5 @@
1
- Copyright (c) 2026, Syl-Studio Inc. c/o Syl. D.
2
-
3
- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
-
1
+ Copyright (c) 2026, Syl-Studio Inc. c/o Syl. D.
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
+
5
5
  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -1,15 +1,52 @@
1
1
  import { DecimalString, Timestamp } from '../common';
2
2
  /**
3
3
  * Leverage configuration for a perpetual position.
4
+ *
5
+ * ============================================================================
6
+ * UNE UNION, PARCE QUE LES DEUX MODES NE PORTENT PAS LES MÊMES CHAMPS
7
+ *
8
+ * `rawUsd` était déclaré **obligatoire** pour les deux modes, et le faux
9
+ * gateway devait donc mentir par une assertion de type pour construire une
10
+ * position cross plausible — exactement ce qu'un double n'a pas le droit de
11
+ * faire.
12
+ *
13
+ * La forme vient du **SDK Python officiel** (`hyperliquid-dex/
14
+ * hyperliquid-python-sdk`, `hyperliquid/utils/types.py`), qui déclare la même
15
+ * union, discriminée sur `type` :
16
+ *
17
+ * CrossLeverage = {"type": Literal["cross"], "value": int}
18
+ * IsolatedLeverage = {"type": Literal["isolated"], "value": int,
19
+ * "rawUsd": str}
20
+ * Leverage = Union[CrossLeverage, IsolatedLeverage]
21
+ *
22
+ * Deux relevés la recoupent, chacun pour une branche : les captures du gateway
23
+ * du 2026-09-22 n'ont que des leviers `cross`, tous en `{ type, value }` nus ;
24
+ * l'exemple de la doc de `clearinghouseState` n'a qu'une position isolée, qui
25
+ * porte `rawUsd: "-95.059824"`. Aucun des deux ne couvre l'autre branche — le
26
+ * SDK, lui, les couvre toutes les deux.
27
+ *
28
+ * Pourquoi l'union et non `rawUsd?: DecimalString` : l'optionnel rendrait
29
+ * `lev.rawUsd` lisible sans vérifier `type`, et `Number(undefined)` rend `NaN`
30
+ * sans un mot. L'union en fait une erreur de compilation. Et si un cross
31
+ * portait un jour `rawUsd`, le coût de s'être trompé ici est nul — personne ne
32
+ * lit ce champ, il serait simplement invisible — là où le coût du `NaN`
33
+ * silencieux se paierait sur un dimensionnement.
34
+ * ============================================================================
4
35
  */
5
- export interface HLPerpLeverage {
6
- /** Notional value in USD used for leverage calculation. */
7
- rawUsd: DecimalString;
36
+ export type HLPerpLeverage = {
8
37
  /** Margin mode used for the position. */
9
- type: 'isolated' | 'cross';
38
+ type: 'cross';
10
39
  /** Current leverage multiplier. */
11
40
  value: number;
12
- }
41
+ } | {
42
+ type: 'isolated';
43
+ value: number;
44
+ /**
45
+ * Notional value in USD used for leverage calculation. Propre à l'isolé,
46
+ * où la marge est cantonnée à la position.
47
+ */
48
+ rawUsd: DecimalString;
49
+ };
13
50
  /**
14
51
  * Funding payments accumulated for a perpetual position.
15
52
  */
@@ -33,8 +70,25 @@ export interface HLPerpPositionDetail {
33
70
  entryPx: DecimalString;
34
71
  /** Leverage configuration. */
35
72
  leverage: HLPerpLeverage;
36
- /** Estimated liquidation price. */
37
- liquidationPx: DecimalString;
73
+ /**
74
+ * Estimated liquidation price, **`null`** quand l'exchange n'en publie pas.
75
+ *
76
+ * Relevé le 2026-09-22 dans les captures réelles du gateway : sur 6 états
77
+ * distincts d'une même paire (`xyz:MU`, cross), les 3 **longs** rendent
78
+ * `null` et les 2 **shorts** rendent une chaîne. Un seul coin sur un seul
79
+ * dex : cette corrélation avec le sens de la position n'explique rien, et je
80
+ * ne connais pas la règle.
81
+ *
82
+ * Ce qui est établi suffit pourtant, et c'est asymétrique : une nullité se
83
+ * prouve par un seul contre-exemple, alors que la non-nullité demanderait de
84
+ * l'avoir épuisée. Trois `null` mesurés closent la question du type ; ils ne
85
+ * disent rien de *quand*.
86
+ *
87
+ * Conséquence pour un consommateur : le `null` s'écarte, il ne se convertit
88
+ * pas. `Number(null)` rend `0`, et un prix de liquidation à zéro est le plus
89
+ * rassurant des mensonges — il paraît infiniment loin du prix courant.
90
+ */
91
+ liquidationPx: DecimalString | null;
38
92
  /** Margin currently used by the position. */
39
93
  marginUsed: DecimalString;
40
94
  /** Maximum allowed leverage for this market. */
@@ -52,7 +106,17 @@ export interface HLPerpPositionDetail {
52
106
  * Wrapper describing a perpetual asset position.
53
107
  */
54
108
  export interface HLPerpAssetPosition {
55
- /** Position details (null if no active position). */
109
+ /**
110
+ * Position details (null if no active position).
111
+ *
112
+ * ⚠️ Laissé nullable bien qu'aucune des entrées relevées le 2026-09-22 ne
113
+ * soit nulle : des observations d'une valeur présente ne prouvent pas qu'elle
114
+ * le soit toujours — et celles-ci portent sur une seule paire —, la doc nomme
115
+ * le cas, et deux des trois lecteurs du bot le gardaient déjà. Le troisième
116
+ * ne le gardait pas, et levait un `Cannot read properties of null` juste
117
+ * après une entrée remplie : c'est ce défaut qui a fait garder ce `| null`
118
+ * plutôt que le resserrer.
119
+ */
56
120
  position: HLPerpPositionDetail | null;
57
121
  /** Position mode configuration. */
58
122
  type: 'oneWay' | 'hedged';
@@ -1,4 +1,26 @@
1
1
  import { DecimalString } from '../common';
2
+ /**
3
+ * ============================================================================
4
+ * DÉCLARER CE QUE L'EXCHANGE REND, PAS CE QU'IL POURRAIT RENDRE
5
+ *
6
+ * Les champs ci-dessous ont été relevés le 2026-10-01 sur les cinq dex vivants
7
+ * (`''`, `xyz`, `para`, `mkts`, `io`) : 434 entrées d'univers et 363 contextes
8
+ * d'actif, via `meta` et `metaAndAssetCtxs`. Les commentaires portent ces
9
+ * comptes parce qu'un type optimiste coûte plus cher qu'un type absent : tant
10
+ * que `midPx` était déclaré `string`, `Number(ctx.midPx)` rendait **`0`** sur
11
+ * les marchés délistés — un prix, pas une erreur.
12
+ *
13
+ * Deux traitements, selon ce que l'absence du champ signifie déjà :
14
+ *
15
+ * - **Drapeau de présence** (`onlyIsolated`, `isDelisted`) : l'absence porte le
16
+ * sens complémentaire, donc une seule valeur est utile et le type la fixe
17
+ * (`?: true`). Écrire `false` serait redondant, et dans un double ce serait
18
+ * rendre une forme que l'exchange ne rend pas.
19
+ * - **Dimension à valeurs** (`growthMode`, `marginMode`, `deployerFeeScale`) :
20
+ * d'autres valeurs sont plausibles même si une seule a été observée, donc le
21
+ * type reste ouvert et le commentaire dit ce qui a été vu.
22
+ * ============================================================================
23
+ */
2
24
  /**
3
25
  * Metadata describing a perpetual market.
4
26
  */
@@ -9,12 +31,71 @@ export interface HLPerpMarketInfo {
9
31
  szDecimals: number;
10
32
  /** Maximum leverage allowed. */
11
33
  maxLeverage: number;
12
- /** Whether the market only supports isolated margin. */
13
- onlyIsolated?: boolean;
14
- /** Whether the market has been delisted. */
15
- isDelisted?: boolean;
16
- /** Optional margin mode restriction. */
34
+ /**
35
+ * Clé de la table de marge qui s'applique à ce marché, à chercher dans
36
+ * `HLPerpMeta.marginTables`.
37
+ *
38
+ * **Requis**, et non optionnel : présent sur **434/434** marchés des cinq
39
+ * dex, délistés compris. Le déclarer optionnel n'évitait aucune erreur — il
40
+ * autorisait seulement des doubles à l'omettre, donc à rendre une forme que
41
+ * l'exchange ne rend jamais.
42
+ */
43
+ marginTableId: number;
44
+ /**
45
+ * Présent **uniquement** sur les marchés restreints à la marge isolée, et
46
+ * toujours accompagné de `marginMode` (0 contre-exemple sur 434).
47
+ *
48
+ * ⚠️ Ce champ n'est **pas** déprécié, contrairement à ce que suggère le dex
49
+ * principal : il n'y survit que sur 8 marchés, tous délistés. Les HIP-3 le
50
+ * rendent partout — `io` 8/8 vivants, `xyz` 90/110, `para` 26/29, soit 126
51
+ * marchés vivants au total. Le marquer `@deprecated` ferait clignoter du
52
+ * code juste.
53
+ *
54
+ * `true` et non `boolean` : les 162 occurrences relevées valent `true`.
55
+ */
56
+ onlyIsolated?: true;
57
+ /**
58
+ * Présent **uniquement** sur les marchés délistés, et toujours à `true` —
59
+ * c'est ainsi que l'exchange marque un marché mort (doc `meta`, exemple
60
+ * `LOOM`). Un marché vivant n'a pas la clé du tout.
61
+ *
62
+ * `true` et non `boolean`, pour que le compilateur refuse
63
+ * `isDelisted: false` : écrit dans un double, il rendrait une forme que le
64
+ * vrai ne rend pas, et c'est précisément ce qu'un double ne doit pas faire.
65
+ */
66
+ isDelisted?: true;
67
+ /**
68
+ * Restriction de mode de marge. Toujours accompagnée de `onlyIsolated`, dans
69
+ * les deux sens. Valeurs relevées : `strictIsolated` sur 52 marchés du dex
70
+ * principal et de `xyz`, `noCross` sur 129 HIP-3.
71
+ */
17
72
  marginMode?: 'strictIsolated' | 'noCross';
73
+ /**
74
+ * Relevé sur les quatre dex HIP-3, jamais sur le principal (0/234).
75
+ *
76
+ * Laissé ouvert (`string`) bien que `'enabled'` soit la seule valeur vue sur
77
+ * 154 marchés : le champ nomme un mode, d'autres états sont plausibles, et il
78
+ * **subsiste sur 13 marchés délistés** — donc il n'est pas tenu à jour et ne
79
+ * dit rien de la vivacité d'un marché. Pour ça, `isDelisted`.
80
+ */
81
+ growthMode?: string;
82
+ /**
83
+ * Multiplicateur appliqué aux frais du déployeur. Relevé sur les quatre dex
84
+ * HIP-3 (200 marchés), jamais sur le principal. Valeurs vues : `'1.0'` et
85
+ * `'0.5'` — laissé ouvert, c'est une échelle.
86
+ */
87
+ deployerFeeScale?: DecimalString;
88
+ /**
89
+ * Date du dernier changement de `deployerFeeScale`.
90
+ *
91
+ * ⚠️ Une **chaîne**, malgré le `Time` du nom, et une chaîne piégeuse :
92
+ * `"2025-11-23T17:37:10.033211662"` — 9 décimales de seconde et **aucun
93
+ * fuseau**. `Date.parse` l'accepte sans broncher, tronque les nanosecondes
94
+ * **et l'interprète en heure locale** : mesuré, cette valeur donne
95
+ * `16:37:10Z` sur une machine en UTC+1 et un autre instant ailleurs. Donc
96
+ * jamais `number`, et pas de parsing sans imposer le fuseau explicitement.
97
+ */
98
+ lastFeeScaleChangeTime?: string;
18
99
  }
19
100
  /**
20
101
  * Margin tier defining leverage limits for a position size range.
@@ -83,53 +164,77 @@ export interface HLPerpMarketUniverse extends HLPerpMarketInfo {
83
164
  export interface HLPerpAssetCtx {
84
165
  /** Daily notional trading volume. */
85
166
  dayNtlVlm: string;
167
+ /**
168
+ * Volume quotidien exprimé dans l'actif de base, là où `dayNtlVlm` l'exprime
169
+ * en notionnel. Relevé sur 363/363 contextes ; il traversait déjà le fil sans
170
+ * qu'aucun type ne le déclare.
171
+ */
172
+ dayBaseVlm: string;
86
173
  /** Current funding rate. */
87
174
  funding: string;
88
- /** Impact price levels used for slippage estimation. */
89
- impactPxs: string[];
90
- /** Mark price used for PnL calculations. */
175
+ /**
176
+ * Prix d'impact estimés, `[bid, ask]`.
177
+ *
178
+ * **`null`** quand l'exchange n'a pas de carnet à montrer : mesuré sur 75/363
179
+ * contextes, tous délistés. Le tableau, lui, fait toujours exactement deux
180
+ * entrées quand il existe (288/288).
181
+ */
182
+ impactPxs: string[] | null;
183
+ /**
184
+ * Mark price used for PnL calculations. Jamais `null` sur les 363 contextes
185
+ * relevés, délistés compris — c'est le seul prix sur lequel on peut compter.
186
+ */
91
187
  markPx: string;
92
- /** Mid price between best bid and ask. */
93
- midPx: string;
188
+ /**
189
+ * Prix milieu du spread, **`null`** sur les mêmes 75 contextes délistés.
190
+ *
191
+ * C'est le champ qui a motivé ce chantier. Déclaré `string`, il faisait
192
+ * rendre `0` à `Number(ctx.midPx)` — et `0` n'est pas une absence de réponse
193
+ * mais une réponse affirmative : dans une comparaison `mid >= oracle`, un
194
+ * `NaN` aurait rendu les deux côtés `false`, `0` tranche. Un consommateur
195
+ * doit donc écarter le `null` avant de calculer, pas le convertir.
196
+ */
197
+ midPx: string | null;
94
198
  /** Total open interest. */
95
199
  openInterest: string;
96
200
  /** Oracle price reference. */
97
201
  oraclePx: string;
98
- /** Price premium relative to the oracle. */
99
- premium: string;
202
+ /** Price premium relative to the oracle. **`null`** sur les mêmes 75. */
203
+ premium: string | null;
100
204
  /** Previous day's closing price. */
101
205
  prevDayPx: string;
102
206
  }
103
207
  /**
104
208
  * Response combining market metadata and runtime context.
209
+ *
210
+ * Le premier élément est le **`HLPerpMeta` complet** : relevé le 2026-10-01 sur
211
+ * `''` et `xyz`, `metaAndAssetCtxs[0]` porte exactement les mêmes trois clés
212
+ * que `meta` — `universe`, `marginTables`, `collateralToken`. Il était typé
213
+ * `{ universe }` seul, ce qui rendait le collatéral du dex illisible depuis cet
214
+ * appel alors qu'il était déjà sur le fil : il fallait un second appel à `meta`
215
+ * pour obtenir une donnée reçue.
105
216
  */
106
- export type HLPerpMetaAndCtx = [{
107
- universe: HLPerpMarketInfo[];
108
- }, HLPerpAssetCtx[]];
217
+ export type HLPerpMetaAndCtx = [HLPerpMeta, HLPerpAssetCtx[]];
109
218
  /**
110
219
  * Normalized representation of a perpetual market used by the SDK.
220
+ *
221
+ * Étend `HLPerpMarketInfo` au lieu d'en recopier les champs : la copie avait
222
+ * déjà divergé — `marginTableId` y était déclaré alors qu'il manquait à la
223
+ * source, et les trois champs HIP-3 traversaient le gateway (`{ ...market }`)
224
+ * sans qu'aucun des deux types ne les connaisse. Un champ ajouté par l'exchange
225
+ * se propage maintenant aux deux formes, ou à aucune.
111
226
  */
112
- export interface HLPerpMarket {
227
+ export interface HLPerpMarket extends HLPerpMarketInfo {
113
228
  /** Market index in the universe list. */
114
229
  index: number;
115
- /** Market symbol (e.g. BTC). */
116
- name: string;
117
- /** Size precision for orders. */
118
- szDecimals: number;
119
- /** Maximum leverage allowed. */
120
- maxLeverage: number;
121
- /** Margin table identifier. */
122
- marginTableId?: number;
123
- /** Whether the market only supports isolated margin mode. */
124
- onlyIsolated?: boolean;
125
- /** Whether the market has been delisted by the exchange. */
126
- isDelisted?: boolean;
127
- /** Optional restriction applied to the market margin mode. */
128
- marginMode?: 'strictIsolated' | 'noCross';
129
230
  /** Current mark price. */
130
231
  markPrice?: DecimalString;
131
- /** Current mid price. */
132
- midPrice?: DecimalString;
232
+ /**
233
+ * Current mid price, `null` quand l'exchange n'en publie pas (marché
234
+ * délisté). `undefined` dit autre chose : la ligne de contexte manquait en
235
+ * face de l'entrée d'univers — deux incidents, deux diagnostics.
236
+ */
237
+ midPrice?: DecimalString | null;
133
238
  /** Current funding rate. */
134
239
  funding?: DecimalString;
135
240
  /** Current open interest. */
@@ -141,8 +246,8 @@ export interface HLPerpMarket {
141
246
  export interface HLPerpMarketExtended extends HLPerpMarket {
142
247
  /** Oracle price reference. */
143
248
  oraclePrice?: DecimalString;
144
- /** Premium vs oracle. */
145
- premium?: DecimalString;
249
+ /** Premium vs oracle, `null` sur un marché sans prix milieu. */
250
+ premium?: DecimalString | null;
146
251
  /** Daily notional volume. */
147
252
  dayNotionalVolume?: DecimalString;
148
253
  /** Estimated bid impact price. */
package/package.json CHANGED
@@ -1,54 +1,54 @@
1
- {
2
- "name": "@syldel/hl-shared-types",
3
- "version": "0.0.21",
4
- "description": "Shared TypeScript interfaces and types for Hyperliquid integration.",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/Syldel/hl-shared-types.git"
10
- },
11
- "bugs": {
12
- "url": "https://github.com/Syldel/hl-shared-types/issues"
13
- },
14
- "homepage": "https://github.com/Syldel/hl-shared-types#readme",
15
- "files": [
16
- "dist"
17
- ],
18
- "scripts": {
19
- "test": "jest",
20
- "lint": "eslint .",
21
- "lint:fix": "eslint . --fix",
22
- "clean": "rm -rf dist",
23
- "build": "npm run lint && npm run test && npm run clean && tsc",
24
- "prepublishOnly": "npm run build",
25
- "release": "npm run build && npm version patch && npm publish"
26
- },
27
- "publishConfig": {
28
- "access": "public"
29
- },
30
- "keywords": [
31
- "hyperliquid",
32
- "trading",
33
- "types",
34
- "interfaces",
35
- "crypto",
36
- "exchange",
37
- "defi"
38
- ],
39
- "author": "Syl. D.",
40
- "license": "ISC",
41
- "devDependencies": {
42
- "@types/jest": "^30.0.0",
43
- "eslint": "^9.39.2",
44
- "eslint-config-prettier": "^10.1.8",
45
- "eslint-plugin-jsonc": "^2.21.0",
46
- "eslint-plugin-prettier": "^5.5.5",
47
- "jest": "^30.5.2",
48
- "jsonc-eslint-parser": "^2.4.2",
49
- "prettier": "^3.8.0",
50
- "ts-jest": "^29.4.13",
51
- "typescript": "^5.3.3",
52
- "typescript-eslint": "^8.53.0"
53
- }
54
- }
1
+ {
2
+ "name": "@syldel/hl-shared-types",
3
+ "version": "0.0.23",
4
+ "description": "Shared TypeScript interfaces and types for Hyperliquid integration.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Syldel/hl-shared-types.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/Syldel/hl-shared-types/issues"
13
+ },
14
+ "homepage": "https://github.com/Syldel/hl-shared-types#readme",
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "test": "jest",
20
+ "lint": "eslint .",
21
+ "lint:fix": "eslint . --fix",
22
+ "clean": "rm -rf dist",
23
+ "build": "npm run lint && npm run test && npm run clean && tsc",
24
+ "prepublishOnly": "npm run build",
25
+ "release": "npm run build && npm version patch && npm publish"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "keywords": [
31
+ "hyperliquid",
32
+ "trading",
33
+ "types",
34
+ "interfaces",
35
+ "crypto",
36
+ "exchange",
37
+ "defi"
38
+ ],
39
+ "author": "Syl. D.",
40
+ "license": "ISC",
41
+ "devDependencies": {
42
+ "@types/jest": "^30.0.0",
43
+ "eslint": "^9.39.2",
44
+ "eslint-config-prettier": "^10.1.8",
45
+ "eslint-plugin-jsonc": "^2.21.0",
46
+ "eslint-plugin-prettier": "^5.5.5",
47
+ "jest": "^30.5.2",
48
+ "jsonc-eslint-parser": "^2.4.2",
49
+ "prettier": "^3.8.0",
50
+ "ts-jest": "^29.4.13",
51
+ "typescript": "^5.3.3",
52
+ "typescript-eslint": "^8.53.0"
53
+ }
54
+ }