@syldel/hl-shared-types 0.0.26 → 0.0.28

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.
@@ -4,12 +4,21 @@ import { AccountAbstractionMode } from './account-abstraction.type';
4
4
  * ============================================================================
5
5
  * LE COLLATÉRAL D'UN MARCHÉ : TROIS RÉPONSES, PAS UN NOMBRE
6
6
  *
7
- * Réponse de `GET /hyperliquid/info/collateral-balance` du gateway.
7
+ * Ce que le **service** du gateway calcule pour un marché donné
8
+ * (`getCollateralBalance(asset)`). La **route**
9
+ * `GET /hyperliquid/info/collateral-balance` répond autre chose :
10
+ * `CollateralBalanceResponse`, plus bas, qui y ajoute `asset`.
8
11
  *
9
12
  * Ce type vit ici parce qu'il **traverse** le réseau : le gateway le produit,
10
13
  * le bot le consomme. Une copie de chaque côté finirait par diverger, et la
11
14
  * divergence porterait sur un chiffre qui dimensionne des ordres.
12
15
  *
16
+ * Aucun des quatre cas ne porte `asset` : l'appelant l'a passé en paramètre et
17
+ * le connaît. Jusqu'au 2026-10-19 deux d'entre eux le rendaient, les deux autres
18
+ * non, et la route l'ajoutait de toute façon : trois sources pour une valeur
19
+ * que personne n'avait besoin de relire dans le résultat, et un faux gateway
20
+ * qui l'avait omis faute d'un type de réponse à compiler contre.
21
+ *
13
22
  * Trois cas, parce que leurs remèdes sont trois : approvisionner le compte,
14
23
  * corriger le nom du marché, ou attendre que le registre soit synchronisé.
15
24
  * Jusqu'au 2026-10-01 les trois rendaient `'0'`, donc aucun n'était
@@ -56,7 +65,6 @@ export type CollateralBalance =
56
65
  | {
57
66
  status: 'unknown-collateral';
58
67
  mode: AccountAbstractionMode;
59
- asset: string;
60
68
  }
61
69
  /**
62
70
  * Le compte est dans un mode dont ce gateway ne sait pas lire le collatéral.
@@ -80,5 +88,28 @@ export type CollateralBalance =
80
88
  | {
81
89
  status: 'unsupported-mode';
82
90
  mode: AccountAbstractionMode;
83
- asset: string;
84
91
  };
92
+ /**
93
+ * Ce que la **route** `GET /hyperliquid/info/collateral-balance` répond : le
94
+ * résultat du service, précédé de `asset`, le marché interrogé (`?asset=`).
95
+ *
96
+ * Le contrôleur du gateway fait `{ asset, ...balance }` quel que soit le
97
+ * statut. Mesuré sur un `ok` le 2026-10-18 (quatre lectures de la capture réelle
98
+ * du bot, ETH, compte unifié) : `asset` y est la première clé. Pour les trois
99
+ * autres statuts, c'est une lecture du contrôleur, pas une mesure.
100
+ *
101
+ * Ce type est celui qu'un client HTTP et un double de la route (le faux gateway
102
+ * du bot) doivent viser : un écart de forme avec la route devient une erreur de
103
+ * compilation, et non une divergence découverte en réel. Un code qui n'a besoin
104
+ * que du résultat garde `CollateralBalance`, dont celui-ci est un sous-type.
105
+ *
106
+ * `asset` est la valeur de `?asset=`, et le gateway refuse en 400 une requête où
107
+ * il manque, est vide ou est répété : toute réponse 200 le porte donc, en chaîne
108
+ * non vide. Ce refus date du 2026-10-19 ; avant lui, une requête sans `?asset=`
109
+ * recevait une réponse sans cette clé (omise par JSON), et ce type était faux
110
+ * pour elle. Éprouvé par les tests du contrôleur, pas contre un gateway qui
111
+ * tourne.
112
+ */
113
+ export type CollateralBalanceResponse = {
114
+ asset: string;
115
+ } & CollateralBalance;
@@ -2,17 +2,39 @@
2
2
  * ============================================================================
3
3
  * LE NOM D'UN MARCHÉ DIT DE QUEL CATALOGUE IL RELÈVE
4
4
  *
5
- * Source : doc Hyperliquid « Asset IDs », relevée le 2026-09-30.
5
+ * Source : doc Hyperliquid « Asset IDs », relevée le 2026-09-30 et relue le
6
+ * 2026-10-19 (avec les pages « Info endpoint », « Spot » et HIP-4, relues le
7
+ * même jour) :
6
8
  *
7
9
  * - un perp déployé par un builder porte **toujours** un nom `{dex}:{coin}` —
8
10
  * la doc écrit « always », ce n'est pas une heuristique ;
9
11
  * - une paire spot se désigne par `10000 + spotInfo.index`, d'où la forme
10
12
  * protocolaire `@<index>` ; seules les canoniques portent `BASE/QUOTE`.
11
13
  * L'exemple HYPE de la doc (token 150, spot 107) recoupe le relevé du
12
- * 2026-09-30 : le solde porte `token: 150`, la paire s'appelle `@107` ;
14
+ * 2026-09-30 : le solde porte `token: 150`, la paire s'appelle `@107`. La
15
+ * page « Info endpoint » le dit : pour le spot, `coin` vaut `PURR/USDC` pour
16
+ * PURR et `@{index}` pour toutes les autres paires ;
17
+ * - un **outcome** (HIP-4) porte un coin `#<encodage>` et un jeton
18
+ * `+<encodage>`, avec `encodage = 10 × outcome + côté` (côté 0 ou 1) et un
19
+ * identifiant d'actif `100 000 000 + encodage` — « Outcomes share most
20
+ * implementation details with spot trading », mais leur représentation par
21
+ * l'API diffère du spot comme des perps. Exemple de la doc : outcome 1, côté
22
+ * 0 → `#10`, `+10`, `100000010`. La page HIP-4, elle, ne décrit aucun format
23
+ * de nom : c'est « Asset IDs » qui le fait ;
13
24
  * - tout le reste est un perp du dex principal, que l'API désigne par un `dex`
14
25
  * vide.
15
26
  *
27
+ * ⚠️ « Tout le reste est un perp » était **faux** pour les outcomes jusqu'au
28
+ * 2026-10-19 : `#10` et `+10` rendaient `perp`. Ils existent sur le mainnet
29
+ * (`outcomeMeta` en rend 329 ce jour-là, dont des contests sportifs et des
30
+ * binaires sur prix), alors que la page HIP-4 ne décrit qu'un premier marché
31
+ * quotidien : la doc est en retard sur le réseau. Un outcome est donc un genre
32
+ * à part, que les consommateurs refusent ou traitent — jamais un perp par défaut.
33
+ *
34
+ * Mesuré le 2026-10-19 (`allPerpMetas` : 544 perps ; `spotMeta` : 334 paires,
35
+ * 508 jetons) : aucun nom ne porte de caractère hors `A-Za-z0-9`, `:`, `/`, `@` ;
36
+ * les noms de paires spot sont 1 `PURR/USDC` et 333 `@N`, aucun autre.
37
+ *
16
38
  * Ce module vit ici, et non dans le bot ou le mobile, pour la même raison que
17
39
  * `tick-and-lot` : qu'il n'en existe **qu'une seule implémentation**. Les deux
18
40
  * doivent décider à l'identique de quel univers relève un marché — sans quoi
@@ -38,7 +60,14 @@ export type HLMarketKind =
38
60
  /** Perp déployé par un builder (HIP-3) : `vntl:ROBOT`, `xyz:AAPL`. */
39
61
  | 'builderPerp'
40
62
  /** Paire spot : `PURR/USDC` (canonique) ou `@107` (forme protocolaire). */
41
- | 'spot';
63
+ | 'spot'
64
+ /**
65
+ * Marché outcome (HIP-4) : coin `#10` ou jeton `+10`, soit `#<encodage>` /
66
+ * `+<encodage>` avec `encodage = 10 × outcome + côté`. Ni un perp, ni une paire
67
+ * spot : son asset id est `100 000 000 + encodage`, et l'API le représente
68
+ * autrement que les deux.
69
+ */
70
+ | 'outcome';
42
71
  /**
43
72
  * Le genre de marché que ce nom désigne, ou `null` quand le nom n'en désigne
44
73
  * aucun.
@@ -3,17 +3,39 @@
3
3
  * ============================================================================
4
4
  * LE NOM D'UN MARCHÉ DIT DE QUEL CATALOGUE IL RELÈVE
5
5
  *
6
- * Source : doc Hyperliquid « Asset IDs », relevée le 2026-09-30.
6
+ * Source : doc Hyperliquid « Asset IDs », relevée le 2026-09-30 et relue le
7
+ * 2026-10-19 (avec les pages « Info endpoint », « Spot » et HIP-4, relues le
8
+ * même jour) :
7
9
  *
8
10
  * - un perp déployé par un builder porte **toujours** un nom `{dex}:{coin}` —
9
11
  * la doc écrit « always », ce n'est pas une heuristique ;
10
12
  * - une paire spot se désigne par `10000 + spotInfo.index`, d'où la forme
11
13
  * protocolaire `@<index>` ; seules les canoniques portent `BASE/QUOTE`.
12
14
  * L'exemple HYPE de la doc (token 150, spot 107) recoupe le relevé du
13
- * 2026-09-30 : le solde porte `token: 150`, la paire s'appelle `@107` ;
15
+ * 2026-09-30 : le solde porte `token: 150`, la paire s'appelle `@107`. La
16
+ * page « Info endpoint » le dit : pour le spot, `coin` vaut `PURR/USDC` pour
17
+ * PURR et `@{index}` pour toutes les autres paires ;
18
+ * - un **outcome** (HIP-4) porte un coin `#<encodage>` et un jeton
19
+ * `+<encodage>`, avec `encodage = 10 × outcome + côté` (côté 0 ou 1) et un
20
+ * identifiant d'actif `100 000 000 + encodage` — « Outcomes share most
21
+ * implementation details with spot trading », mais leur représentation par
22
+ * l'API diffère du spot comme des perps. Exemple de la doc : outcome 1, côté
23
+ * 0 → `#10`, `+10`, `100000010`. La page HIP-4, elle, ne décrit aucun format
24
+ * de nom : c'est « Asset IDs » qui le fait ;
14
25
  * - tout le reste est un perp du dex principal, que l'API désigne par un `dex`
15
26
  * vide.
16
27
  *
28
+ * ⚠️ « Tout le reste est un perp » était **faux** pour les outcomes jusqu'au
29
+ * 2026-10-19 : `#10` et `+10` rendaient `perp`. Ils existent sur le mainnet
30
+ * (`outcomeMeta` en rend 329 ce jour-là, dont des contests sportifs et des
31
+ * binaires sur prix), alors que la page HIP-4 ne décrit qu'un premier marché
32
+ * quotidien : la doc est en retard sur le réseau. Un outcome est donc un genre
33
+ * à part, que les consommateurs refusent ou traitent — jamais un perp par défaut.
34
+ *
35
+ * Mesuré le 2026-10-19 (`allPerpMetas` : 544 perps ; `spotMeta` : 334 paires,
36
+ * 508 jetons) : aucun nom ne porte de caractère hors `A-Za-z0-9`, `:`, `/`, `@` ;
37
+ * les noms de paires spot sont 1 `PURR/USDC` et 333 `@N`, aucun autre.
38
+ *
17
39
  * Ce module vit ici, et non dans le bot ou le mobile, pour la même raison que
18
40
  * `tick-and-lot` : qu'il n'en existe **qu'une seule implémentation**. Les deux
19
41
  * doivent décider à l'identique de quel univers relève un marché — sans quoi
@@ -35,6 +57,10 @@
35
57
  Object.defineProperty(exports, "__esModule", { value: true });
36
58
  exports.hlMarketKind = hlMarketKind;
37
59
  exports.hlPerpDexOf = hlPerpDexOf;
60
+ /** `#` (coin) ou `+` (jeton) en tête : la marque d'un outcome, jamais celle d'un perp. */
61
+ const OUTCOME_MARK = /^[#+]/;
62
+ /** La forme documentée : `#<encodage>` ou `+<encodage>`, l'encodage étant un entier. */
63
+ const OUTCOME_NAME = /^[#+]\d+$/;
38
64
  /**
39
65
  * Le genre de marché que ce nom désigne, ou `null` quand le nom n'en désigne
40
66
  * aucun.
@@ -46,6 +72,14 @@ function hlMarketKind(name) {
46
72
  // (`cash:XYZ/USDT`), et c'est alors la paire qui prime sur le dex.
47
73
  if (name.includes('/') || name.startsWith('@'))
48
74
  return 'spot';
75
+ // Un outcome : `#` ou `+`, puis l'encodage — un entier, rien d'autre. Strict à
76
+ // dessein : `#` seul, `#1a` ou `+-3` ne sont pas la forme documentée, et ne sont
77
+ // donc **pas** reconnus comme outcome. Ils ne sont pas non plus des perps : ces
78
+ // deux marques ne désignent jamais un perp, donc un nom qui les porte hors de la
79
+ // forme documentée est un **doute** (`null`), pas « tout le reste ». Doit précéder
80
+ // la règle « tout le reste est un perp ».
81
+ if (OUTCOME_MARK.test(name))
82
+ return OUTCOME_NAME.test(name) ? 'outcome' : null;
49
83
  const separator = name.indexOf(':');
50
84
  if (separator === -1)
51
85
  return 'perp';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syldel/hl-shared-types",
3
- "version": "0.0.26",
3
+ "version": "0.0.28",
4
4
  "description": "Shared TypeScript interfaces and types for Hyperliquid integration.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",