@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.
- package/dist/chunk-4UEJOM6W.mjs +9 -0
- package/dist/index.d.mts +462 -0
- package/dist/index.d.ts +462 -2
- package/dist/index.js +37465 -2
- package/dist/index.mjs +1027 -0
- package/dist/lib/cloid.d.ts.map +1 -1
- package/dist/lib/cloid.js +11 -1
- package/dist/lib/cloid.js.map +1 -1
- package/dist/meta-52Q5UUQ4.mjs +1474 -0
- package/dist/meta-FTWJX4LV.mjs +1445 -0
- package/dist/meta-IKWYLG3Q.mjs +1316 -0
- package/dist/meta-UUXKK7IB.mjs +1355 -0
- package/dist/perpDexs-PSE3LEVV.mjs +9 -0
- package/dist/perpDexs-S3TK25EU.mjs +17 -0
- package/dist/perpDexs-TZIQ57IW.mjs +537 -0
- package/dist/perpDexs-YNEAJ3R5.mjs +7 -0
- package/dist/perpDexs-YS3QQSHW.mjs +338 -0
- package/dist/spotMeta-7IJT3W6H.mjs +6442 -0
- package/dist/spotMeta-LEO5QFNS.mjs +26392 -0
- package/dist/spotMeta-MC5UYLQ7.mjs +6335 -0
- package/dist/spotMeta-TXJWYTKI.mjs +26403 -0
- package/dist/spotMeta-VAANYV77.mjs +6346 -0
- package/dist/spotMeta-ZVBZNUUE.mjs +26559 -0
- package/index.ts +6 -0
- package/lib/cloid/README.md +233 -0
- package/lib/cloid/cloid.ts +368 -0
- package/lib/cloid/encoder.ts +60 -0
- package/lib/constants/fee.ts +2 -0
- package/lib/constants/tokens.ts +28 -0
- package/lib/fee.ts +105 -0
- package/lib/hip3/market-info.ts +25 -0
- package/lib/hip3/utils.ts +9 -0
- package/lib/meta/README.md +471 -0
- package/lib/meta/data/mainnet/meta.json +1462 -0
- package/lib/meta/data/mainnet/perpDexs.json +11 -0
- package/lib/meta/data/mainnet/spotMeta.json +6432 -0
- package/lib/meta/data/testnet/dexs/rrrrr.json +33 -0
- package/lib/meta/data/testnet/meta.json +1343 -0
- package/lib/meta/data/testnet/perpDexs.json +531 -0
- package/lib/meta/data/testnet/spotMeta.json +26547 -0
- package/lib/meta/metadata.ts +600 -0
- package/lib/pup/calculator.ts +221 -0
- package/lib/pup/index.ts +9 -0
- package/lib/pup/types.ts +94 -0
- package/lib/utils/formatter.ts +97 -0
- package/package.json +21 -17
- 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,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
|