@basedone/core 0.0.1 → 0.0.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.
Files changed (47) hide show
  1. package/dist/chunk-4UEJOM6W.mjs +9 -0
  2. package/dist/index.d.mts +462 -0
  3. package/dist/index.d.ts +462 -2
  4. package/dist/index.js +37465 -2
  5. package/dist/index.mjs +1027 -0
  6. package/dist/lib/cloid.d.ts.map +1 -1
  7. package/dist/lib/cloid.js +11 -1
  8. package/dist/lib/cloid.js.map +1 -1
  9. package/dist/meta-52Q5UUQ4.mjs +1474 -0
  10. package/dist/meta-FTWJX4LV.mjs +1445 -0
  11. package/dist/meta-IKWYLG3Q.mjs +1316 -0
  12. package/dist/meta-UUXKK7IB.mjs +1355 -0
  13. package/dist/perpDexs-PSE3LEVV.mjs +9 -0
  14. package/dist/perpDexs-S3TK25EU.mjs +17 -0
  15. package/dist/perpDexs-TZIQ57IW.mjs +537 -0
  16. package/dist/perpDexs-YNEAJ3R5.mjs +7 -0
  17. package/dist/perpDexs-YS3QQSHW.mjs +338 -0
  18. package/dist/spotMeta-7IJT3W6H.mjs +6442 -0
  19. package/dist/spotMeta-LEO5QFNS.mjs +26392 -0
  20. package/dist/spotMeta-MC5UYLQ7.mjs +6335 -0
  21. package/dist/spotMeta-TXJWYTKI.mjs +26403 -0
  22. package/dist/spotMeta-VAANYV77.mjs +6346 -0
  23. package/dist/spotMeta-ZVBZNUUE.mjs +26559 -0
  24. package/index.ts +6 -0
  25. package/lib/cloid/README.md +233 -0
  26. package/lib/cloid/cloid.ts +368 -0
  27. package/lib/cloid/encoder.ts +60 -0
  28. package/lib/constants/fee.ts +2 -0
  29. package/lib/constants/tokens.ts +28 -0
  30. package/lib/fee.ts +105 -0
  31. package/lib/hip3/market-info.ts +25 -0
  32. package/lib/hip3/utils.ts +9 -0
  33. package/lib/meta/README.md +471 -0
  34. package/lib/meta/data/mainnet/meta.json +1462 -0
  35. package/lib/meta/data/mainnet/perpDexs.json +11 -0
  36. package/lib/meta/data/mainnet/spotMeta.json +6432 -0
  37. package/lib/meta/data/testnet/dexs/rrrrr.json +33 -0
  38. package/lib/meta/data/testnet/meta.json +1343 -0
  39. package/lib/meta/data/testnet/perpDexs.json +531 -0
  40. package/lib/meta/data/testnet/spotMeta.json +26547 -0
  41. package/lib/meta/metadata.ts +600 -0
  42. package/lib/pup/calculator.ts +221 -0
  43. package/lib/pup/index.ts +9 -0
  44. package/lib/pup/types.ts +94 -0
  45. package/lib/utils/formatter.ts +97 -0
  46. package/package.json +21 -17
  47. package/readme.md +0 -0
package/lib/fee.ts ADDED
@@ -0,0 +1,105 @@
1
+ // Global limits on Hyperliquid
2
+
3
+ import { BASED_FEE_WALLET, BASED_REFERRAL_CODE } from "./constants/fee";
4
+
5
+ // builder fee in 1/10 of a basis point
6
+ const TARGET_SPOT_BUILDER_FEE = 100; // 0.1%
7
+ const TARGET_FUTURES_BUILDER_FEE = 25; // 0.025%
8
+
9
+ export const TARGET_APPROVED_MAX_BUILDER_FEE = Math.max(
10
+ TARGET_SPOT_BUILDER_FEE,
11
+ TARGET_FUTURES_BUILDER_FEE,
12
+ ); // 0.1%
13
+ export const TARGET_APPROVED_MAX_BUILDER_FEE_PERCENT = `0.1%`;
14
+
15
+ /**
16
+ * Get the amount of builder fee to approve for
17
+ * @param perpetualTradingFee - Perp fee in percentage
18
+ * @param spotTradingFee - Spot fee in percentage
19
+ * @returns {
20
+ * amount: number; - The amount of builder fee to approve in 1/10 of a basis point
21
+ * percent: string; - eg. String input to approve fee, "0.1%"
22
+ * }
23
+ */
24
+ export const getApprovalAmount = ({
25
+ customFeeEnabled,
26
+ perpetualTradingFee,
27
+ spotTradingFee,
28
+ }: {
29
+ customFeeEnabled: boolean;
30
+ perpetualTradingFee?: number;
31
+ spotTradingFee?: number;
32
+ }): {
33
+ approvalAmount: number;
34
+ approvalPercent: `${string}%`;
35
+ perpFee: number;
36
+ spotFee: number;
37
+ builder: `0x${string}`;
38
+ referralCode: string;
39
+ } => {
40
+ if (!customFeeEnabled) {
41
+ return {
42
+ approvalAmount: TARGET_APPROVED_MAX_BUILDER_FEE,
43
+ perpFee: TARGET_FUTURES_BUILDER_FEE,
44
+ spotFee: TARGET_SPOT_BUILDER_FEE,
45
+ approvalPercent: TARGET_APPROVED_MAX_BUILDER_FEE_PERCENT,
46
+ builder: BASED_FEE_WALLET,
47
+ referralCode: BASED_REFERRAL_CODE,
48
+ };
49
+ }
50
+ let validatedPerpFeePct = perpetualTradingFee;
51
+ if (validatedPerpFeePct === undefined) {
52
+ validatedPerpFeePct = TARGET_FUTURES_BUILDER_FEE / 1000;
53
+ }
54
+
55
+ // ensure perp fee is either 0 or between 0.01% and 0.1%
56
+ if (validatedPerpFeePct > 0 && validatedPerpFeePct < 0.01) {
57
+ console.warn("Perp fee is less than 0.01%, setting to 0.01%");
58
+ validatedPerpFeePct = 0.01;
59
+ }
60
+ if (validatedPerpFeePct < 0) {
61
+ console.warn("Perp fee is less than 0, setting to 0");
62
+ validatedPerpFeePct = 0;
63
+ }
64
+ if (validatedPerpFeePct > 0.1) {
65
+ console.warn("Perp fee is greater than 0.1%, setting to 0.1%");
66
+ validatedPerpFeePct = 0.1;
67
+ }
68
+
69
+ let validatedSpotFeePct = spotTradingFee;
70
+
71
+ if (validatedSpotFeePct === undefined) {
72
+ validatedSpotFeePct = TARGET_SPOT_BUILDER_FEE / 1000;
73
+ }
74
+ if (validatedSpotFeePct > 0 && validatedSpotFeePct < 0.025) {
75
+ console.warn("Spot fee is less than 0.025%, setting to 0.025%");
76
+ validatedSpotFeePct = 0.025;
77
+ }
78
+ if (validatedSpotFeePct < 0) {
79
+ console.warn("Spot fee is less than 0, setting to 0");
80
+ validatedSpotFeePct = 0;
81
+ }
82
+ if (validatedSpotFeePct > 1) {
83
+ console.warn("Spot fee is greater than 1%, setting to 1%");
84
+ validatedSpotFeePct = 1;
85
+ }
86
+
87
+ const perpFee = Math.floor(validatedPerpFeePct * 1000);
88
+ const spotFee = Math.floor(validatedSpotFeePct * 1000);
89
+
90
+ const requiredPercent =
91
+ validatedPerpFeePct > validatedSpotFeePct
92
+ ? validatedPerpFeePct
93
+ : validatedSpotFeePct;
94
+ const requiredAmount = Math.max(Math.floor(requiredPercent * 1000), 1); // 1/10 of a basis point (returned from Hyperliquid)
95
+ const requiredPercentString = `${requiredAmount / 1000}%`;
96
+
97
+ return {
98
+ approvalAmount: requiredAmount,
99
+ approvalPercent: requiredPercentString as `${string}%`,
100
+ perpFee: perpFee,
101
+ spotFee: spotFee,
102
+ builder: BASED_FEE_WALLET,
103
+ referralCode: BASED_REFERRAL_CODE,
104
+ };
105
+ };
@@ -0,0 +1,25 @@
1
+ // static HIP-3 market info
2
+
3
+ import { SpotToken, PerpDex } from "@nktkas/hyperliquid";
4
+ import { TESTNET_USDC_SPOT_TOKEN, USDC_SPOT_TOKEN } from "../constants/tokens";
5
+
6
+ // currently /info type: perpDexs doesn't include collateral token
7
+ interface HIP3DexInfo extends PerpDex {
8
+ collateralToken: SpotToken;
9
+ }
10
+
11
+ export const HIP3_DEX_INFO: Record<string, HIP3DexInfo> = {};
12
+
13
+ const TESTNET_DEX_INFO: Record<string, HIP3DexInfo> = {
14
+ vtnls: {
15
+ collateralToken: TESTNET_USDC_SPOT_TOKEN,
16
+ name: "vntls",
17
+ full_name: "Ventuals",
18
+ deployer: "0xc65008a70f511ae0407d26022ff1516422acea94",
19
+ oracle_updater: null,
20
+ },
21
+ };
22
+
23
+ function getHIP3DexInfo(dex: string, isTestnet: boolean) {
24
+ return isTestnet ? TESTNET_DEX_INFO[dex] : HIP3_DEX_INFO[dex];
25
+ }
@@ -0,0 +1,9 @@
1
+ export function isHip3Symbol(symbol: string) {
2
+ if (!symbol) return false;
3
+ return symbol.includes(":");
4
+ }
5
+
6
+ export function getHip3Dex(symbol: string) {
7
+ if (!symbol) return null;
8
+ return symbol.split(":")[0];
9
+ }
@@ -0,0 +1,471 @@
1
+ # MetadataClient
2
+
3
+ A robust utility for handling Hyperliquid metadata and symbol-to-asset conversions for trading operations.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Type-safe symbol to asset ID conversion** for perps, spot, and HIP-3 markets
8
+ - ✅ **Multi-quote asset support** (USDC, USDT0, USDH, etc.) - no hardcoded assumptions
9
+ - ✅ **Correct asset ID calculation** per Hyperliquid spec
10
+ - ✅ **Decimal precision information** for sizes and prices
11
+ - ✅ **Market discovery** - find all trading pairs for a base token
12
+ - ✅ **Case-insensitive lookups**
13
+ - ✅ **Lazy initialization** support
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pnpm add @basedone/core
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### Mainnet (default)
24
+
25
+ ```typescript
26
+ import { MetadataClient } from "@basedone/core";
27
+ import { InfoClient, HttpTransport } from "@nktkas/hyperliquid";
28
+
29
+ // Setup for mainnet
30
+ const transport = new HttpTransport();
31
+ const infoClient = new InfoClient({ transport });
32
+ const metadata = new MetadataClient(infoClient);
33
+
34
+ // Initialize (fetches metadata from Hyperliquid)
35
+ await metadata.initialize();
36
+
37
+ // Get market info for trading
38
+ const btc = metadata.getPerpsMarket("BTC");
39
+ console.log(btc);
40
+ // {
41
+ // symbol: "BTC",
42
+ // assetId: 0,
43
+ // szDecimals: 5,
44
+ // type: "perps",
45
+ // maxLeverage: 40
46
+ // }
47
+
48
+ const purrUsdc = metadata.getSpotMarket("PURR", "USDC");
49
+ console.log(purrUsdc);
50
+ // {
51
+ // symbol: "PURR/USDC",
52
+ // assetId: 10000,
53
+ // szDecimals: 0,
54
+ // priceDecimals: 8,
55
+ // type: "spot",
56
+ // baseToken: "PURR",
57
+ // quoteToken: "USDC"
58
+ // }
59
+ ```
60
+
61
+ ### Testnet
62
+
63
+ ```typescript
64
+ // Setup for testnet
65
+ const transport = new HttpTransport({
66
+ url: "https://api.hyperliquid-testnet.xyz/info"
67
+ });
68
+ const infoClient = new InfoClient({ transport });
69
+
70
+ // Enable testnet mode
71
+ const metadata = new MetadataClient(infoClient, {
72
+ isTestnet: true
73
+ });
74
+
75
+ await metadata.initialize();
76
+
77
+ // Network info
78
+ console.log(metadata.getNetworkInfo());
79
+ // { isTestnet: true, useStaticFallback: true, initialized: true }
80
+ ```
81
+
82
+ ### Static Fallback (Offline Mode)
83
+
84
+ The client includes bundled static metadata that's used as a fallback when API requests fail.
85
+
86
+ ```typescript
87
+ // Enable static fallback (default: true)
88
+ const metadata = new MetadataClient(infoClient, {
89
+ useStaticFallback: true // Falls back to bundled data if API fails
90
+ });
91
+
92
+ // Or disable fallback (throws error if API fails)
93
+ const metadata = new MetadataClient(infoClient, {
94
+ useStaticFallback: false
95
+ });
96
+ ```
97
+
98
+ ## Asset ID Calculation
99
+
100
+ The MetadataClient correctly calculates asset IDs according to Hyperliquid's spec:
101
+
102
+ ### Perpetuals
103
+ ```
104
+ assetId = index in meta universe
105
+ ```
106
+ Example: BTC is always index 0, so assetId = 0
107
+
108
+ ### Spot Markets
109
+ ```
110
+ assetId = 10000 + index
111
+ ```
112
+ Example: PURR/USDC at index 0 → assetId = 10000
113
+
114
+ ### HIP-3 (Builder Deployed Perps)
115
+ ```
116
+ assetId = 100000 + (perp_dex_index * 10000) + index_in_meta
117
+ ```
118
+ Example: vntls:ABC at dex index 1, market index 0 → assetId = 110000
119
+
120
+ ## API Reference
121
+
122
+ ### Constructor
123
+
124
+ ```typescript
125
+ new MetadataClient(infoClient: InfoClient, config?: {
126
+ hip3Dexs?: string[]; // List of HIP-3 DEXs to load
127
+ lazyInit?: boolean; // Auto-initialize on first use
128
+ isTestnet?: boolean; // Use testnet (default: false)
129
+ useStaticFallback?: boolean; // Fallback to static data if API fails (default: true)
130
+ })
131
+ ```
132
+
133
+ ### Core Methods
134
+
135
+ #### `initialize(): Promise<void>`
136
+ Fetches metadata from Hyperliquid. Call this once at startup.
137
+
138
+ ```typescript
139
+ await metadata.initialize();
140
+ ```
141
+
142
+ #### `getMarketBySymbol(symbol: string, quoteAsset?: string): Promise<MarketInfo | null>`
143
+ Universal method to get market info. Handles perps, spot, and HIP-3 formats.
144
+
145
+ ```typescript
146
+ // Perps
147
+ const btc = await metadata.getMarketBySymbol("BTC");
148
+
149
+ // Spot with default USDC quote
150
+ const purr = await metadata.getMarketBySymbol("PURR/USDC");
151
+
152
+ // Spot with custom quote
153
+ const hype = await metadata.getMarketBySymbol("HYPE", "USDT0");
154
+
155
+ // HIP-3
156
+ const hip3 = await metadata.getMarketBySymbol("vntls:ABC");
157
+ ```
158
+
159
+ #### `getPerpsMarket(symbol: string): MarketInfo | null`
160
+ Get perpetuals market info.
161
+
162
+ ```typescript
163
+ const eth = metadata.getPerpsMarket("ETH");
164
+ // { symbol: "ETH", assetId: 1, szDecimals: 4, type: "perps", maxLeverage: 25 }
165
+ ```
166
+
167
+ #### `getSpotMarket(baseSymbol: string, quoteSymbol?: string): MarketInfo | null`
168
+ Get spot market info. Defaults to USDC quote if not specified.
169
+
170
+ ```typescript
171
+ const purrUsdc = metadata.getSpotMarket("PURR", "USDC");
172
+ const hypeUsdt = metadata.getSpotMarket("HYPE", "USDT0");
173
+ ```
174
+
175
+ #### `getAllMarketsForBase(baseSymbol: string): MarketInfo[]`
176
+ Get all trading pairs for a base token (useful for multi-quote scenarios).
177
+
178
+ ```typescript
179
+ const hypeMarkets = metadata.getAllMarketsForBase("HYPE");
180
+ // [
181
+ // { symbol: "HYPE/USDC", assetId: 10107, ... },
182
+ // { symbol: "HYPE/USDT0", assetId: 10207, ... },
183
+ // { symbol: "HYPE/USDH", assetId: 10232, ... }
184
+ // ]
185
+ ```
186
+
187
+ #### `getTokenInfo(tokenSymbol: string): TokenInfo | null`
188
+ Get detailed token information.
189
+
190
+ ```typescript
191
+ const usdc = metadata.getTokenInfo("USDC");
192
+ // {
193
+ // name: "USDC",
194
+ // index: 0,
195
+ // szDecimals: 8,
196
+ // weiDecimals: 8,
197
+ // tokenId: "0x6d1e7cde53ba9467b783cb7c530ce054"
198
+ // }
199
+ ```
200
+
201
+ #### `getAvailableQuoteAssets(): string[]`
202
+ Get all quote assets available in the system.
203
+
204
+ ```typescript
205
+ const quotes = metadata.getAvailableQuoteAssets();
206
+ // ["USDC", "USDT0", "USDH"]
207
+ ```
208
+
209
+ #### `getNetworkInfo(): { isTestnet: boolean; useStaticFallback: boolean; initialized: boolean }`
210
+ Get network configuration and initialization status.
211
+
212
+ ```typescript
213
+ const info = metadata.getNetworkInfo();
214
+ // {
215
+ // isTestnet: false,
216
+ // useStaticFallback: true,
217
+ // initialized: true
218
+ // }
219
+ ```
220
+
221
+ ## Types
222
+
223
+ ### `MarketInfo`
224
+
225
+ ```typescript
226
+ interface MarketInfo {
227
+ symbol: string; // "BTC" or "PURR/USDC"
228
+ assetId: number; // Trading asset ID
229
+ szDecimals: number; // Size decimals for orders
230
+ type: "perps" | "spot" | "hip3";
231
+ maxLeverage?: number; // For perps only
232
+
233
+ // Spot-specific
234
+ baseToken?: string; // "PURR"
235
+ quoteToken?: string; // "USDC"
236
+ priceDecimals?: number; // Price decimals (quote token's szDecimals)
237
+
238
+ // HIP-3 specific
239
+ dexName?: string; // "vntls"
240
+ dexIndex?: number; // DEX index
241
+ }
242
+ ```
243
+
244
+ ### `TokenInfo`
245
+
246
+ ```typescript
247
+ interface TokenInfo {
248
+ name: string;
249
+ index: number;
250
+ szDecimals: number;
251
+ weiDecimals: number;
252
+ tokenId: string;
253
+ }
254
+ ```
255
+
256
+ ## Usage Examples
257
+
258
+ ### Trading Order Preparation
259
+
260
+ ```typescript
261
+ // Get market info
262
+ const market = await metadata.getMarketBySymbol("BTC");
263
+
264
+ // Prepare order with correct decimals
265
+ const order = {
266
+ asset: market.assetId, // 0
267
+ size: "0.001", // Respects szDecimals (5 for BTC)
268
+ price: "50000.0", // Max 5 sig figs, max 6-szDecimals decimals
269
+ isBuy: true,
270
+ };
271
+ ```
272
+
273
+ ### Multi-Quote Asset Selection
274
+
275
+ ```typescript
276
+ // Find all markets for HYPE
277
+ const markets = metadata.getAllMarketsForBase("HYPE");
278
+
279
+ // Show user all available quote options
280
+ markets.forEach(m => {
281
+ console.log(`${m.baseToken}/${m.quoteToken} - Asset ID: ${m.assetId}`);
282
+ });
283
+
284
+ // User selects USDT0
285
+ const selected = markets.find(m => m.quoteToken === "USDT0");
286
+ ```
287
+
288
+ ### Decimal Validation
289
+
290
+ ```typescript
291
+ const market = metadata.getSpotMarket("PURR", "USDC");
292
+
293
+ // Validate order size
294
+ function validateSize(size: string, szDecimals: number): boolean {
295
+ const parts = size.split(".");
296
+ if (parts.length > 1) {
297
+ return parts[1].length <= szDecimals;
298
+ }
299
+ return true;
300
+ }
301
+
302
+ console.log(validateSize("1.001", market.szDecimals)); // false (szDecimals = 0)
303
+ console.log(validateSize("1", market.szDecimals)); // true
304
+ ```
305
+
306
+ ### Price Decimal Rules
307
+
308
+ Per Hyperliquid spec:
309
+ - **Perps**: Max 5 significant figures, max `6 - szDecimals` decimal places
310
+ - **Spot**: Max 5 significant figures, max `8 - szDecimals` decimal places
311
+ - Integers always allowed regardless of sig figs
312
+
313
+ ```typescript
314
+ const btc = metadata.getPerpsMarket("BTC"); // szDecimals = 5
315
+ // Valid prices: 1234.5, 0.001234, 123456 (integer)
316
+ // Invalid: 1234.56 (too many sig figs), 0.0012345 (> 6-5 = 1 decimal place)
317
+
318
+ const purr = metadata.getSpotMarket("PURR"); // szDecimals = 0
319
+ // Valid prices: 0.0001234 (8 decimal places max for spot)
320
+ // Invalid: 0.00001234 (> 8-0 = 8 decimal places)
321
+ ```
322
+
323
+ ## Migration from SymbolMappingUtil
324
+
325
+ ### Before (SymbolMappingUtil)
326
+ ```typescript
327
+ // ❌ Hardcoded USDC assumption
328
+ const pair = `${token}/USDC`;
329
+ const assetId = symbolMapping.getSpotAssetId(pair);
330
+
331
+ // ❌ Error-prone with inconsistent usage
332
+ symbolMapping.resolveSingleToken(token, "spot");
333
+ ```
334
+
335
+ ### After (MetadataClient)
336
+ ```typescript
337
+ // ✅ Explicit quote asset handling
338
+ const market = await metadata.getMarketBySymbol(token, quoteAsset);
339
+ const assetId = market.assetId;
340
+
341
+ // ✅ Type-safe, consistent API
342
+ const allMarkets = metadata.getAllMarketsForBase(token);
343
+ ```
344
+
345
+ ## Best Practices
346
+
347
+ 1. **Initialize once at startup**
348
+ ```typescript
349
+ const metadata = new MetadataClient(infoClient);
350
+ await metadata.initialize();
351
+ ```
352
+
353
+ 2. **Configure network appropriately**
354
+ ```typescript
355
+ // For testnet
356
+ const metadata = new MetadataClient(testnetInfoClient, {
357
+ isTestnet: true,
358
+ useStaticFallback: true // Recommended for reliability
359
+ });
360
+
361
+ // For production
362
+ const metadata = new MetadataClient(mainnetInfoClient, {
363
+ isTestnet: false,
364
+ useStaticFallback: true // Fallback to static data if API unavailable
365
+ });
366
+ ```
367
+
368
+ 3. **Always specify quote asset for spot markets**
369
+ ```typescript
370
+ // Good
371
+ const market = metadata.getSpotMarket("HYPE", "USDC");
372
+
373
+ // Also acceptable (defaults to USDC)
374
+ const market = metadata.getSpotMarket("HYPE");
375
+ ```
376
+
377
+ 4. **Use getMarketBySymbol for user input**
378
+ ```typescript
379
+ // Handles both "BTC" (perps) and "PURR/USDC" (spot)
380
+ const market = await metadata.getMarketBySymbol(userInput);
381
+ ```
382
+
383
+ 5. **Cache metadata, not individual results**
384
+ ```typescript
385
+ // ✅ Good - metadata is cached
386
+ await metadata.initialize();
387
+
388
+ // ✅ Also good - lookups are fast in-memory
389
+ const btc1 = metadata.getPerpsMarket("BTC");
390
+ const btc2 = metadata.getPerpsMarket("BTC");
391
+ ```
392
+
393
+ 6. **Show all quote options to users**
394
+ ```typescript
395
+ const markets = metadata.getAllMarketsForBase(baseToken);
396
+ const quotes = metadata.getAvailableQuoteAssets();
397
+ ```
398
+
399
+ 7. **Handle initialization errors gracefully**
400
+ ```typescript
401
+ try {
402
+ await metadata.initialize();
403
+ } catch (error) {
404
+ // With useStaticFallback: true, this should rarely happen
405
+ // It means both API fetch AND static data loading failed
406
+ console.error("Failed to load metadata:", error);
407
+ }
408
+
409
+ // Check if initialized
410
+ const { initialized } = metadata.getNetworkInfo();
411
+ if (!initialized) {
412
+ console.error("Metadata not ready");
413
+ }
414
+ ```
415
+
416
+ ## Error Handling
417
+
418
+ ```typescript
419
+ const market = await metadata.getMarketBySymbol("INVALID");
420
+ if (!market) {
421
+ console.error("Market not found");
422
+ return;
423
+ }
424
+
425
+ // Type-safe access
426
+ if (market.type === "spot") {
427
+ console.log(`Quote: ${market.quoteToken}`);
428
+ } else if (market.type === "perps") {
429
+ console.log(`Leverage: ${market.maxLeverage}x`);
430
+ }
431
+ ```
432
+
433
+ ## HIP-3 Support
434
+
435
+ ```typescript
436
+ // Load HIP-3 DEX metadata
437
+ const metadata = new MetadataClient(infoClient, {
438
+ hip3Dexs: ["vntls", "another-dex"]
439
+ });
440
+ await metadata.initialize();
441
+
442
+ // Get HIP-3 market
443
+ const hip3Market = metadata.getHip3Market("vntls:ABC");
444
+ // {
445
+ // symbol: "vntls:ABC",
446
+ // assetId: 110000, // 100000 + 1*10000 + 0
447
+ // szDecimals: 5,
448
+ // type: "hip3",
449
+ // dexName: "vntls",
450
+ // dexIndex: 1
451
+ // }
452
+ ```
453
+
454
+ ## Testing
455
+
456
+ The package includes comprehensive tests covering:
457
+ - Perpetuals market lookups
458
+ - Spot market lookups with multiple quotes
459
+ - Asset ID calculations
460
+ - Token information retrieval
461
+ - Market discovery
462
+ - Edge cases and error handling
463
+
464
+ Run tests:
465
+ ```bash
466
+ pnpm test
467
+ ```
468
+
469
+ ## License
470
+
471
+ ISC