@pear-protocol/hyperliquid-sdk 0.0.79 → 0.1.0
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/README.md +1141 -0
- package/dist/clients/hyperliquid.d.ts +1 -1
- package/dist/clients/positions.d.ts +2 -2
- package/dist/clients/watchlist.d.ts +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/useMarket.d.ts +8 -0
- package/dist/hooks/useMarketData.d.ts +1 -4
- package/dist/hooks/useTrading.d.ts +1 -2
- package/dist/index.d.ts +26 -123
- package/dist/index.js +550 -1194
- package/dist/store/hyperliquidDataStore.d.ts +3 -7
- package/dist/store/marketDataStore.d.ts +1 -3
- package/dist/store/tokenSelectionMetadataStore.d.ts +2 -4
- package/dist/types.d.ts +12 -31
- package/dist/utils/market-symbol.d.ts +5 -0
- package/dist/utils/position-processor.d.ts +2 -2
- package/dist/utils/token-metadata-extractor.d.ts +5 -9
- package/dist/utils/token-metadata-selectors.d.ts +4 -0
- package/package.json +1 -1
- package/dist/hooks/useWebData.d.ts +0 -30
- package/dist/utils/symbol-translator.d.ts +0 -40
package/README.md
ADDED
|
@@ -0,0 +1,1141 @@
|
|
|
1
|
+
# @pear-protocol/hyperliquid-sdk
|
|
2
|
+
|
|
3
|
+
A comprehensive React SDK for integrating with Pear Protocol's Hyperliquid trading platform. Build perpetual trading interfaces with real-time WebSocket data, position management, and market analytics.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Provider Setup](#provider-setup)
|
|
10
|
+
- [Authentication](#authentication)
|
|
11
|
+
- [Hooks Reference](#hooks-reference)
|
|
12
|
+
- [Trading Hooks](#trading-hooks)
|
|
13
|
+
- [Data Hooks](#data-hooks)
|
|
14
|
+
- [WebSocket Hooks](#websocket-hooks)
|
|
15
|
+
- [UI State Hooks](#ui-state-hooks)
|
|
16
|
+
- [Client Functions](#client-functions)
|
|
17
|
+
- [Utility Functions](#utility-functions)
|
|
18
|
+
- [Types Reference](#types-reference)
|
|
19
|
+
- [Examples](#examples)
|
|
20
|
+
- [Contributing](#contributing)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @pear-protocol/hyperliquid-sdk
|
|
28
|
+
# or
|
|
29
|
+
yarn add @pear-protocol/hyperliquid-sdk
|
|
30
|
+
# or
|
|
31
|
+
pnpm add @pear-protocol/hyperliquid-sdk
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Peer Dependencies
|
|
35
|
+
|
|
36
|
+
Ensure you have React 18+ installed:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install react react-dom
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import {
|
|
48
|
+
PearHyperliquidProvider,
|
|
49
|
+
useAuth,
|
|
50
|
+
usePosition,
|
|
51
|
+
useMarket,
|
|
52
|
+
} from '@pear-protocol/hyperliquid-sdk';
|
|
53
|
+
|
|
54
|
+
function App() {
|
|
55
|
+
return (
|
|
56
|
+
<PearHyperliquidProvider
|
|
57
|
+
apiBaseUrl="https://hl-ui.pearprotocol.io"
|
|
58
|
+
wsUrl="wss://hl-ui.pearprotocol.io/ws"
|
|
59
|
+
clientId="YOUR_CLIENT_ID"
|
|
60
|
+
>
|
|
61
|
+
<TradingApp />
|
|
62
|
+
</PearHyperliquidProvider>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function TradingApp() {
|
|
67
|
+
const { isAuthenticated, loginWithSignedMessage } = useAuth();
|
|
68
|
+
const { openPositions, createPosition } = usePosition();
|
|
69
|
+
const { allTokenMetadata } = useMarket();
|
|
70
|
+
|
|
71
|
+
if (!isAuthenticated) {
|
|
72
|
+
return <button onClick={() => loginWithSignedMessage(address, signMessage)}>Connect</button>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<div>
|
|
77
|
+
<h2>Open Positions: {openPositions?.length ?? 0}</h2>
|
|
78
|
+
{/* Your trading UI */}
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Provider Setup
|
|
87
|
+
|
|
88
|
+
Wrap your application with `PearHyperliquidProvider` to enable SDK functionality:
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import { PearHyperliquidProvider } from '@pear-protocol/hyperliquid-sdk';
|
|
92
|
+
|
|
93
|
+
function App() {
|
|
94
|
+
return (
|
|
95
|
+
<PearHyperliquidProvider
|
|
96
|
+
apiBaseUrl="https://hl-ui.pearprotocol.io" // Pear API endpoint
|
|
97
|
+
wsUrl="wss://hl-ui.pearprotocol.io/ws" // WebSocket endpoint
|
|
98
|
+
clientId="YOUR_CLIENT_ID" // Your application ID
|
|
99
|
+
>
|
|
100
|
+
{children}
|
|
101
|
+
</PearHyperliquidProvider>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Provider Props
|
|
107
|
+
|
|
108
|
+
| Prop | Type | Default | Description |
|
|
109
|
+
|------|------|---------|-------------|
|
|
110
|
+
| `apiBaseUrl` | `string` | `'https://hl-ui.pearprotocol.io'` | Pear Protocol API base URL |
|
|
111
|
+
| `wsUrl` | `string` | `'wss://hl-ui.pearprotocol.io/ws'` | WebSocket server URL |
|
|
112
|
+
| `clientId` | `string` | `'PEARPROTOCOLUI'` | Application client identifier |
|
|
113
|
+
|
|
114
|
+
### Accessing Provider Context
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
import { usePearHyperliquid } from '@pear-protocol/hyperliquid-sdk';
|
|
118
|
+
|
|
119
|
+
function Component() {
|
|
120
|
+
const {
|
|
121
|
+
clientId,
|
|
122
|
+
apiBaseUrl,
|
|
123
|
+
wsUrl,
|
|
124
|
+
isConnected, // Pear WebSocket connection status
|
|
125
|
+
lastError, // Pear WebSocket last error
|
|
126
|
+
nativeIsConnected, // Hyperliquid WebSocket connection status
|
|
127
|
+
nativeLastError, // Hyperliquid WebSocket last error
|
|
128
|
+
} = usePearHyperliquid();
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Authentication
|
|
135
|
+
|
|
136
|
+
The SDK provides EIP-712 signature-based authentication:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
import { useAuth } from '@pear-protocol/hyperliquid-sdk';
|
|
140
|
+
|
|
141
|
+
function AuthComponent() {
|
|
142
|
+
const {
|
|
143
|
+
isAuthenticated,
|
|
144
|
+
isLoading,
|
|
145
|
+
address,
|
|
146
|
+
loginWithSignedMessage,
|
|
147
|
+
loginWithPrivy,
|
|
148
|
+
logout,
|
|
149
|
+
} = useAuth();
|
|
150
|
+
|
|
151
|
+
const handleLogin = async () => {
|
|
152
|
+
// Using wallet signature (e.g., with wagmi/viem)
|
|
153
|
+
await loginWithSignedMessage(
|
|
154
|
+
walletAddress,
|
|
155
|
+
async (message) => {
|
|
156
|
+
// Your wallet's signMessage function
|
|
157
|
+
return await signMessage({ message });
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const handlePrivyLogin = async () => {
|
|
163
|
+
// Using Privy authentication
|
|
164
|
+
await loginWithPrivy(walletAddress, privyAccessToken);
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Authentication Flow
|
|
170
|
+
|
|
171
|
+
1. SDK requests an EIP-712 message from the server
|
|
172
|
+
2. User signs the message with their wallet
|
|
173
|
+
3. Signature is sent to authenticate
|
|
174
|
+
4. JWT tokens are stored and managed automatically
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Hooks Reference
|
|
179
|
+
|
|
180
|
+
### Trading Hooks
|
|
181
|
+
|
|
182
|
+
#### `usePosition()`
|
|
183
|
+
|
|
184
|
+
Manage positions with full CRUD operations:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import { usePosition } from '@pear-protocol/hyperliquid-sdk';
|
|
188
|
+
|
|
189
|
+
function PositionManager() {
|
|
190
|
+
const {
|
|
191
|
+
openPositions, // OpenPositionDto[] - enriched position data
|
|
192
|
+
isLoading,
|
|
193
|
+
createPosition, // Create new position
|
|
194
|
+
closePosition, // Close specific position
|
|
195
|
+
closeAllPositions, // Close all positions
|
|
196
|
+
adjustPosition, // Rebalance position weights
|
|
197
|
+
updateRiskParameters, // Update TP/SL
|
|
198
|
+
updateLeverage, // Change leverage
|
|
199
|
+
} = usePosition();
|
|
200
|
+
|
|
201
|
+
const handleCreate = async () => {
|
|
202
|
+
await createPosition({
|
|
203
|
+
longAssets: [{ asset: 'BTC', weight: 50 }, { asset: 'ETH', weight: 50 }],
|
|
204
|
+
shortAssets: [{ asset: 'SOL', weight: 100 }],
|
|
205
|
+
usdValue: 1000,
|
|
206
|
+
leverage: 5,
|
|
207
|
+
slippage: 0.5,
|
|
208
|
+
executionType: 'MARKET',
|
|
209
|
+
takeProfit: { triggerType: 'WEIGHTED_RATIO', triggerValue: 1.1 },
|
|
210
|
+
stopLoss: { triggerType: 'WEIGHTED_RATIO', triggerValue: 0.95 },
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### `useTrading()`
|
|
217
|
+
|
|
218
|
+
High-level trading operations with enriched trade history:
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
import { useTrading } from '@pear-protocol/hyperliquid-sdk';
|
|
222
|
+
|
|
223
|
+
function TradingComponent() {
|
|
224
|
+
const {
|
|
225
|
+
tradeHistories, // Trade history with metadata
|
|
226
|
+
createTrade,
|
|
227
|
+
adjustTrade,
|
|
228
|
+
closeTrade,
|
|
229
|
+
} = useTrading();
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
#### `useOrders()`
|
|
234
|
+
|
|
235
|
+
Manage open and trigger orders:
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
import { useOrders } from '@pear-protocol/hyperliquid-sdk';
|
|
239
|
+
|
|
240
|
+
function OrdersComponent() {
|
|
241
|
+
const {
|
|
242
|
+
openOrders, // OpenLimitOrderDto[]
|
|
243
|
+
triggerOrders, // Filtered trigger orders
|
|
244
|
+
adjustOrder,
|
|
245
|
+
cancelOrder,
|
|
246
|
+
} = useOrders();
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
#### `useSpotOrder()`
|
|
251
|
+
|
|
252
|
+
Execute spot swaps:
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
import { useSpotOrder } from '@pear-protocol/hyperliquid-sdk';
|
|
256
|
+
|
|
257
|
+
function SpotSwap() {
|
|
258
|
+
const { executeSpotOrder, isLoading } = useSpotOrder();
|
|
259
|
+
|
|
260
|
+
const handleSwap = async () => {
|
|
261
|
+
await executeSpotOrder({
|
|
262
|
+
fromAsset: 'USDC',
|
|
263
|
+
toAsset: 'USDH',
|
|
264
|
+
amount: 100,
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
#### `useTwap()`
|
|
271
|
+
|
|
272
|
+
Monitor TWAP order execution:
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
import { useTwap } from '@pear-protocol/hyperliquid-sdk';
|
|
276
|
+
|
|
277
|
+
function TwapMonitor() {
|
|
278
|
+
const { twapOrders, cancelTwapOrder } = useTwap();
|
|
279
|
+
|
|
280
|
+
return twapOrders?.map(order => (
|
|
281
|
+
<div key={order.orderId}>
|
|
282
|
+
Status: {order.status}
|
|
283
|
+
Progress: {order.filledUsdValue} / {order.totalUsdValue}
|
|
284
|
+
Chunks: {order.chunks.length - order.remainingChunks} / {order.chunks.length}
|
|
285
|
+
</div>
|
|
286
|
+
));
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Data Hooks
|
|
291
|
+
|
|
292
|
+
#### `useMarket()`
|
|
293
|
+
|
|
294
|
+
Access token metadata:
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
import { useMarket } from '@pear-protocol/hyperliquid-sdk';
|
|
298
|
+
|
|
299
|
+
function MarketData() {
|
|
300
|
+
const {
|
|
301
|
+
allTokenMetadata, // Record<string, TokenMetadata>
|
|
302
|
+
getAssetByName, // (symbol: string) => TokenMetadata | null
|
|
303
|
+
} = useMarket();
|
|
304
|
+
|
|
305
|
+
const btcData = getAssetByName('BTC');
|
|
306
|
+
// { symbol: 'BTC', currentPrice: 50000, maxLeverage: 50, ... }
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
#### `useMarketData()`
|
|
311
|
+
|
|
312
|
+
Get market baskets and analytics:
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
import { useMarketData, type CollateralFilter } from '@pear-protocol/hyperliquid-sdk';
|
|
316
|
+
|
|
317
|
+
function MarketAnalytics() {
|
|
318
|
+
const {
|
|
319
|
+
activeBaskets, // Active market baskets
|
|
320
|
+
highlightedBaskets, // Featured baskets
|
|
321
|
+
topGainers, // Best performing
|
|
322
|
+
topLosers, // Worst performing
|
|
323
|
+
watchlistBaskets, // User's watchlist
|
|
324
|
+
isLoading,
|
|
325
|
+
collateralFilter, // 'ALL' | 'USDC' | 'USDH'
|
|
326
|
+
setCollateralFilter,
|
|
327
|
+
} = useMarketData();
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
#### `useAllUserBalances()`
|
|
332
|
+
|
|
333
|
+
Aggregate user balances:
|
|
334
|
+
|
|
335
|
+
```tsx
|
|
336
|
+
import { useAllUserBalances } from '@pear-protocol/hyperliquid-sdk';
|
|
337
|
+
|
|
338
|
+
function BalanceDisplay() {
|
|
339
|
+
const {
|
|
340
|
+
spotUsdcBalance,
|
|
341
|
+
spotUsdhBalance,
|
|
342
|
+
availableToTradeUsdc,
|
|
343
|
+
availableToTradeUsdh,
|
|
344
|
+
isLoading,
|
|
345
|
+
} = useAllUserBalances();
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
#### `useTokenSelectionMetadata()`
|
|
350
|
+
|
|
351
|
+
Get computed metrics for selected tokens:
|
|
352
|
+
|
|
353
|
+
```tsx
|
|
354
|
+
import { useTokenSelectionMetadata } from '@pear-protocol/hyperliquid-sdk';
|
|
355
|
+
|
|
356
|
+
function SelectionMetrics() {
|
|
357
|
+
const {
|
|
358
|
+
longTokensMetadata, // Metadata for selected long tokens
|
|
359
|
+
shortTokensMetadata, // Metadata for selected short tokens
|
|
360
|
+
weightedRatio, // Current weighted price ratio
|
|
361
|
+
weightedRatio24h, // 24h weighted ratio change
|
|
362
|
+
priceRatio, // Simple price ratio
|
|
363
|
+
openInterest, // Combined OI
|
|
364
|
+
volume, // Combined volume
|
|
365
|
+
maxLeverage, // Maximum allowed leverage
|
|
366
|
+
minMargin, // Minimum margin required
|
|
367
|
+
leverageMatched, // If all tokens support same leverage
|
|
368
|
+
isPriceDataReady,
|
|
369
|
+
} = useTokenSelectionMetadata();
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
#### `useHistoricalPriceData()`
|
|
374
|
+
|
|
375
|
+
Fetch and cache historical prices:
|
|
376
|
+
|
|
377
|
+
```tsx
|
|
378
|
+
import { useHistoricalPriceData } from '@pear-protocol/hyperliquid-sdk';
|
|
379
|
+
|
|
380
|
+
function ChartData() {
|
|
381
|
+
const { fetchHistoricalData, getCachedData } = useHistoricalPriceData();
|
|
382
|
+
|
|
383
|
+
useEffect(() => {
|
|
384
|
+
fetchHistoricalData('BTC', startTime, endTime, '1h');
|
|
385
|
+
}, []);
|
|
386
|
+
|
|
387
|
+
const candles = getCachedData('BTC', startTime, endTime);
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### `useBasketCandles()`
|
|
392
|
+
|
|
393
|
+
Compose weighted candles from multiple assets:
|
|
394
|
+
|
|
395
|
+
```tsx
|
|
396
|
+
import { useBasketCandles } from '@pear-protocol/hyperliquid-sdk';
|
|
397
|
+
|
|
398
|
+
function BasketChart() {
|
|
399
|
+
const {
|
|
400
|
+
basketCandles, // Weighted OHLCV data
|
|
401
|
+
isLoading,
|
|
402
|
+
error,
|
|
403
|
+
} = useBasketCandles({
|
|
404
|
+
longAssets: [{ symbol: 'BTC', weight: 50 }, { symbol: 'ETH', weight: 50 }],
|
|
405
|
+
shortAssets: [{ symbol: 'SOL', weight: 100 }],
|
|
406
|
+
interval: '1h',
|
|
407
|
+
leverage: 5,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### WebSocket Hooks
|
|
413
|
+
|
|
414
|
+
#### `useHyperliquidWebSocket()`
|
|
415
|
+
|
|
416
|
+
Access Pear API WebSocket (managed by provider):
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
import { useHyperliquidWebSocket } from '@pear-protocol/hyperliquid-sdk';
|
|
420
|
+
|
|
421
|
+
// Typically used internally by the provider
|
|
422
|
+
const { isConnected, lastError } = useHyperliquidWebSocket({
|
|
423
|
+
wsUrl: 'wss://hl-ui.pearprotocol.io/ws',
|
|
424
|
+
address: userAddress,
|
|
425
|
+
enabled: true,
|
|
426
|
+
});
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### `useHyperliquidNativeWebSocket()`
|
|
430
|
+
|
|
431
|
+
Access Hyperliquid native WebSocket (managed by provider):
|
|
432
|
+
|
|
433
|
+
```tsx
|
|
434
|
+
import { useHyperliquidNativeWebSocket } from '@pear-protocol/hyperliquid-sdk';
|
|
435
|
+
|
|
436
|
+
// Typically used internally by the provider
|
|
437
|
+
const { isConnected, lastError } = useHyperliquidNativeWebSocket({
|
|
438
|
+
address: userAddress,
|
|
439
|
+
tokens: ['BTC', 'ETH'],
|
|
440
|
+
enabled: true,
|
|
441
|
+
});
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### UI State Hooks
|
|
445
|
+
|
|
446
|
+
#### `useUserSelection()`
|
|
447
|
+
|
|
448
|
+
Manage token selection state:
|
|
449
|
+
|
|
450
|
+
```tsx
|
|
451
|
+
import { useUserSelection } from '@pear-protocol/hyperliquid-sdk';
|
|
452
|
+
|
|
453
|
+
function TokenSelector() {
|
|
454
|
+
const {
|
|
455
|
+
longTokens, // TokenSelection[]
|
|
456
|
+
shortTokens, // TokenSelection[]
|
|
457
|
+
addToken, // (isLong: boolean) => boolean
|
|
458
|
+
removeToken, // (isLong: boolean, index: number) => void
|
|
459
|
+
updateTokenWeight, // (isLong: boolean, index: number, weight: number) => void
|
|
460
|
+
handleTokenSelect, // (symbol: string) => void
|
|
461
|
+
setTokenSelections, // (longs, shorts) => void
|
|
462
|
+
resetToDefaults, // Reset to default selection
|
|
463
|
+
candleInterval, // Current chart interval
|
|
464
|
+
setCandleInterval, // Set chart interval
|
|
465
|
+
openTokenSelector, // Token selector modal state
|
|
466
|
+
setOpenTokenSelector,
|
|
467
|
+
conflicts, // Detected token conflicts
|
|
468
|
+
openConflictModal,
|
|
469
|
+
} = useUserSelection();
|
|
470
|
+
|
|
471
|
+
const handleAddLong = () => {
|
|
472
|
+
const success = addToken(true); // Returns false if MAX_ASSETS_PER_LEG reached
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
#### `useWatchlist()`
|
|
478
|
+
|
|
479
|
+
Manage user watchlist:
|
|
480
|
+
|
|
481
|
+
```tsx
|
|
482
|
+
import { useWatchlist } from '@pear-protocol/hyperliquid-sdk';
|
|
483
|
+
|
|
484
|
+
function Watchlist() {
|
|
485
|
+
const { toggleWatchlist, isLoading } = useWatchlist();
|
|
486
|
+
|
|
487
|
+
const handleToggle = async () => {
|
|
488
|
+
await toggleWatchlist(
|
|
489
|
+
[{ asset: 'BTC', weight: 50 }],
|
|
490
|
+
[{ asset: 'ETH', weight: 50 }]
|
|
491
|
+
);
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
#### `useNotifications()`
|
|
497
|
+
|
|
498
|
+
Access user notifications:
|
|
499
|
+
|
|
500
|
+
```tsx
|
|
501
|
+
import { useNotifications } from '@pear-protocol/hyperliquid-sdk';
|
|
502
|
+
|
|
503
|
+
function NotificationCenter() {
|
|
504
|
+
const {
|
|
505
|
+
notifications, // NotificationDto[]
|
|
506
|
+
markAsRead,
|
|
507
|
+
isLoading,
|
|
508
|
+
} = useNotifications();
|
|
509
|
+
}
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
#### `useAccountSummary()`
|
|
513
|
+
|
|
514
|
+
Get portfolio-level metrics:
|
|
515
|
+
|
|
516
|
+
```tsx
|
|
517
|
+
import { useAccountSummary } from '@pear-protocol/hyperliquid-sdk';
|
|
518
|
+
|
|
519
|
+
function AccountOverview() {
|
|
520
|
+
const { accountSummary } = useAccountSummary();
|
|
521
|
+
|
|
522
|
+
return (
|
|
523
|
+
<div>
|
|
524
|
+
<p>Account Value: ${accountSummary?.totalAccountValue}</p>
|
|
525
|
+
<p>Unrealized PnL: ${accountSummary?.totalUnrealizedPnl}</p>
|
|
526
|
+
<p>Margin Used: ${accountSummary?.totalMarginUsed}</p>
|
|
527
|
+
</div>
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## Client Functions
|
|
535
|
+
|
|
536
|
+
The SDK exports client functions for direct API calls:
|
|
537
|
+
|
|
538
|
+
### Position Operations
|
|
539
|
+
|
|
540
|
+
```tsx
|
|
541
|
+
import {
|
|
542
|
+
createPosition,
|
|
543
|
+
closePosition,
|
|
544
|
+
closeAllPositions,
|
|
545
|
+
adjustPosition,
|
|
546
|
+
updateRiskParameters,
|
|
547
|
+
updateLeverage,
|
|
548
|
+
} from '@pear-protocol/hyperliquid-sdk';
|
|
549
|
+
|
|
550
|
+
// Create position
|
|
551
|
+
const result = await createPosition(apiBaseUrl, {
|
|
552
|
+
longAssets: [{ asset: 'BTC', weight: 100 }],
|
|
553
|
+
shortAssets: [{ asset: 'ETH', weight: 100 }],
|
|
554
|
+
usdValue: 1000,
|
|
555
|
+
leverage: 5,
|
|
556
|
+
slippage: 0.5,
|
|
557
|
+
executionType: 'MARKET',
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
// Close position
|
|
561
|
+
await closePosition(apiBaseUrl, positionId, { slippage: 0.5 });
|
|
562
|
+
|
|
563
|
+
// Update TP/SL
|
|
564
|
+
await updateRiskParameters(apiBaseUrl, positionId, {
|
|
565
|
+
takeProfit: { triggerType: 'WEIGHTED_RATIO', triggerValue: 1.1 },
|
|
566
|
+
stopLoss: { triggerType: 'WEIGHTED_RATIO', triggerValue: 0.95 },
|
|
567
|
+
});
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
### Order Operations
|
|
571
|
+
|
|
572
|
+
```tsx
|
|
573
|
+
import {
|
|
574
|
+
adjustOrder,
|
|
575
|
+
cancelOrder,
|
|
576
|
+
cancelTwapOrder,
|
|
577
|
+
executeSpotOrder,
|
|
578
|
+
} from '@pear-protocol/hyperliquid-sdk';
|
|
579
|
+
|
|
580
|
+
// Adjust limit order
|
|
581
|
+
await adjustOrder(apiBaseUrl, orderId, { limitRatio: 1.05 });
|
|
582
|
+
|
|
583
|
+
// Cancel order
|
|
584
|
+
await cancelOrder(apiBaseUrl, orderId);
|
|
585
|
+
|
|
586
|
+
// Execute spot swap
|
|
587
|
+
await executeSpotOrder(apiBaseUrl, {
|
|
588
|
+
fromAsset: 'USDC',
|
|
589
|
+
toAsset: 'USDH',
|
|
590
|
+
amount: 100,
|
|
591
|
+
});
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
### Watchlist Operations
|
|
595
|
+
|
|
596
|
+
```tsx
|
|
597
|
+
import { toggleWatchlist } from '@pear-protocol/hyperliquid-sdk';
|
|
598
|
+
|
|
599
|
+
await toggleWatchlist(apiBaseUrl, longAssets, shortAssets);
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## Utility Functions
|
|
605
|
+
|
|
606
|
+
### Position Validation
|
|
607
|
+
|
|
608
|
+
```tsx
|
|
609
|
+
import {
|
|
610
|
+
validateMinimumAssetSize,
|
|
611
|
+
validateMaxAssetsPerLeg,
|
|
612
|
+
calculateMinimumPositionValue,
|
|
613
|
+
MINIMUM_ASSET_USD_VALUE, // 11
|
|
614
|
+
MAX_ASSETS_PER_LEG, // 15
|
|
615
|
+
} from '@pear-protocol/hyperliquid-sdk';
|
|
616
|
+
|
|
617
|
+
try {
|
|
618
|
+
validateMinimumAssetSize(usdValue, longAssets, shortAssets);
|
|
619
|
+
validateMaxAssetsPerLeg(longAssets, shortAssets);
|
|
620
|
+
} catch (error) {
|
|
621
|
+
if (error instanceof MinimumPositionSizeError) {
|
|
622
|
+
console.log('Minimum:', error.minimumRequired);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const minValue = calculateMinimumPositionValue(longAssets, shortAssets);
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
### Token Metadata
|
|
630
|
+
|
|
631
|
+
```tsx
|
|
632
|
+
import {
|
|
633
|
+
TokenMetadataExtractor,
|
|
634
|
+
selectTokenMetadataBySymbols,
|
|
635
|
+
getAssetByName,
|
|
636
|
+
} from '@pear-protocol/hyperliquid-sdk';
|
|
637
|
+
|
|
638
|
+
// Extract metadata for a token
|
|
639
|
+
const metadata = TokenMetadataExtractor.extractTokenMetadata(
|
|
640
|
+
'BTC',
|
|
641
|
+
perpMetaAssets,
|
|
642
|
+
assetContexts,
|
|
643
|
+
allMids,
|
|
644
|
+
activeAssetData
|
|
645
|
+
);
|
|
646
|
+
|
|
647
|
+
// Filter metadata by symbols
|
|
648
|
+
const filtered = selectTokenMetadataBySymbols(allMetadata, ['BTC', 'ETH']);
|
|
649
|
+
|
|
650
|
+
// Single lookup
|
|
651
|
+
const btc = getAssetByName(allMetadata, 'BTC');
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### Basket Calculations
|
|
655
|
+
|
|
656
|
+
```tsx
|
|
657
|
+
import {
|
|
658
|
+
computeBasketCandles,
|
|
659
|
+
calculateWeightedRatio,
|
|
660
|
+
createCandleLookups,
|
|
661
|
+
getCompleteTimestamps,
|
|
662
|
+
} from '@pear-protocol/hyperliquid-sdk';
|
|
663
|
+
|
|
664
|
+
// Compute weighted basket candles
|
|
665
|
+
const basketCandles = computeBasketCandles(
|
|
666
|
+
longAssets,
|
|
667
|
+
shortAssets,
|
|
668
|
+
longCandles,
|
|
669
|
+
shortCandles,
|
|
670
|
+
leverage
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
// Calculate weighted price ratio
|
|
674
|
+
const ratio = calculateWeightedRatio(longMetadata, shortMetadata, weights);
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
### Chart Interval Conversion
|
|
678
|
+
|
|
679
|
+
```tsx
|
|
680
|
+
import {
|
|
681
|
+
mapTradingViewIntervalToCandleInterval,
|
|
682
|
+
mapCandleIntervalToTradingViewInterval,
|
|
683
|
+
} from '@pear-protocol/hyperliquid-sdk';
|
|
684
|
+
|
|
685
|
+
const candleInterval = mapTradingViewIntervalToCandleInterval('60'); // '1h'
|
|
686
|
+
const tvInterval = mapCandleIntervalToTradingViewInterval('1h'); // '60'
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Order Helpers
|
|
690
|
+
|
|
691
|
+
```tsx
|
|
692
|
+
import {
|
|
693
|
+
getOrderLeverage,
|
|
694
|
+
getOrderUsdValue,
|
|
695
|
+
getOrderTriggerType,
|
|
696
|
+
getOrderTriggerValue,
|
|
697
|
+
getOrderDirection,
|
|
698
|
+
isBtcDomOrder,
|
|
699
|
+
} from '@pear-protocol/hyperliquid-sdk';
|
|
700
|
+
|
|
701
|
+
const leverage = getOrderLeverage(order);
|
|
702
|
+
const isBtcDom = isBtcDomOrder(order);
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
### Conflict Detection
|
|
706
|
+
|
|
707
|
+
```tsx
|
|
708
|
+
import { ConflictDetector } from '@pear-protocol/hyperliquid-sdk';
|
|
709
|
+
|
|
710
|
+
const conflicts = ConflictDetector.detectConflicts(longTokens, shortTokens);
|
|
711
|
+
// Returns TokenConflict[] if same token appears in both legs
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
## Types Reference
|
|
717
|
+
|
|
718
|
+
### Core Types
|
|
719
|
+
|
|
720
|
+
```tsx
|
|
721
|
+
import type {
|
|
722
|
+
// API Response
|
|
723
|
+
ApiResponse,
|
|
724
|
+
ApiErrorResponse,
|
|
725
|
+
|
|
726
|
+
// Position Types
|
|
727
|
+
OpenPositionDto,
|
|
728
|
+
PositionAssetDetailDto,
|
|
729
|
+
|
|
730
|
+
// Order Types
|
|
731
|
+
OpenLimitOrderDto,
|
|
732
|
+
OrderAssetDto,
|
|
733
|
+
OrderStatus,
|
|
734
|
+
TriggerOrderNotificationType,
|
|
735
|
+
|
|
736
|
+
// Market Data
|
|
737
|
+
TokenMetadata,
|
|
738
|
+
ActiveAssetsResponse,
|
|
739
|
+
ActiveAssetGroupItem,
|
|
740
|
+
|
|
741
|
+
// WebSocket
|
|
742
|
+
WebSocketConnectionState,
|
|
743
|
+
WebSocketChannel,
|
|
744
|
+
|
|
745
|
+
// Token Selection
|
|
746
|
+
TokenSelection,
|
|
747
|
+
TokenConflict,
|
|
748
|
+
|
|
749
|
+
// Candles
|
|
750
|
+
CandleInterval,
|
|
751
|
+
CandleData,
|
|
752
|
+
|
|
753
|
+
// TWAP
|
|
754
|
+
TwapMonitoringDto,
|
|
755
|
+
|
|
756
|
+
// Account
|
|
757
|
+
PlatformAccountSummaryResponseDto,
|
|
758
|
+
} from '@pear-protocol/hyperliquid-sdk';
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### Key Type Definitions
|
|
762
|
+
|
|
763
|
+
```tsx
|
|
764
|
+
interface TokenMetadata {
|
|
765
|
+
symbol: string;
|
|
766
|
+
assetName: string;
|
|
767
|
+
currentPrice: number;
|
|
768
|
+
priceChange24h: number;
|
|
769
|
+
priceChangePercent24h: number;
|
|
770
|
+
maxLeverage: number;
|
|
771
|
+
openInterest: string;
|
|
772
|
+
volume: string;
|
|
773
|
+
fundingRate: number;
|
|
774
|
+
isAtOiCaps: boolean;
|
|
775
|
+
collateralToken?: 'USDC' | 'USDH';
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
interface OpenPositionDto {
|
|
779
|
+
positionId: string;
|
|
780
|
+
longAssets: PositionAssetDetailDto[];
|
|
781
|
+
shortAssets: PositionAssetDetailDto[];
|
|
782
|
+
entryRatio: number;
|
|
783
|
+
markRatio: number;
|
|
784
|
+
marginUsed: number;
|
|
785
|
+
positionValue: number;
|
|
786
|
+
unrealizedPnl: number;
|
|
787
|
+
takeProfit?: TpSlOrderParameters;
|
|
788
|
+
stopLoss?: TpSlOrderParameters;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
type TriggerOrderNotificationType =
|
|
792
|
+
| 'PRICE'
|
|
793
|
+
| 'PRICE_RATIO'
|
|
794
|
+
| 'WEIGHTED_RATIO'
|
|
795
|
+
| 'CROSS_ASSET_PRICE'
|
|
796
|
+
| 'PREDICTION_MARKET_OUTCOME'
|
|
797
|
+
| 'BTC_DOM';
|
|
798
|
+
|
|
799
|
+
type CandleInterval = '1m' | '5m' | '15m' | '1h' | '4h' | '1d';
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
---
|
|
803
|
+
|
|
804
|
+
## Examples
|
|
805
|
+
|
|
806
|
+
### Complete Trading Flow
|
|
807
|
+
|
|
808
|
+
```tsx
|
|
809
|
+
import {
|
|
810
|
+
PearHyperliquidProvider,
|
|
811
|
+
useAuth,
|
|
812
|
+
usePosition,
|
|
813
|
+
useMarket,
|
|
814
|
+
useUserSelection,
|
|
815
|
+
useTokenSelectionMetadata,
|
|
816
|
+
useAllUserBalances,
|
|
817
|
+
} from '@pear-protocol/hyperliquid-sdk';
|
|
818
|
+
|
|
819
|
+
function TradingInterface() {
|
|
820
|
+
const { isAuthenticated, loginWithSignedMessage } = useAuth();
|
|
821
|
+
const { openPositions, createPosition, closePosition } = usePosition();
|
|
822
|
+
const { allTokenMetadata } = useMarket();
|
|
823
|
+
const { longTokens, shortTokens, addToken, updateTokenWeight } = useUserSelection();
|
|
824
|
+
const { weightedRatio, maxLeverage, minMargin } = useTokenSelectionMetadata();
|
|
825
|
+
const { availableToTradeUsdc } = useAllUserBalances();
|
|
826
|
+
|
|
827
|
+
const handleCreatePosition = async () => {
|
|
828
|
+
try {
|
|
829
|
+
await createPosition({
|
|
830
|
+
longAssets: longTokens.map(t => ({ asset: t.symbol, weight: t.weight })),
|
|
831
|
+
shortAssets: shortTokens.map(t => ({ asset: t.symbol, weight: t.weight })),
|
|
832
|
+
usdValue: 1000,
|
|
833
|
+
leverage: Math.min(maxLeverage, 10),
|
|
834
|
+
slippage: 0.5,
|
|
835
|
+
executionType: 'MARKET',
|
|
836
|
+
});
|
|
837
|
+
} catch (error) {
|
|
838
|
+
console.error('Failed to create position:', error);
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
return (
|
|
843
|
+
<div>
|
|
844
|
+
<h2>Available: ${availableToTradeUsdc}</h2>
|
|
845
|
+
<h3>Weighted Ratio: {weightedRatio.toFixed(4)}</h3>
|
|
846
|
+
<h3>Max Leverage: {maxLeverage}x</h3>
|
|
847
|
+
|
|
848
|
+
<div>
|
|
849
|
+
<h4>Long Tokens</h4>
|
|
850
|
+
{longTokens.map((token, i) => (
|
|
851
|
+
<div key={i}>
|
|
852
|
+
{token.symbol}: {token.weight}%
|
|
853
|
+
@ ${allTokenMetadata[token.symbol]?.currentPrice}
|
|
854
|
+
</div>
|
|
855
|
+
))}
|
|
856
|
+
</div>
|
|
857
|
+
|
|
858
|
+
<button onClick={handleCreatePosition}>
|
|
859
|
+
Open Position
|
|
860
|
+
</button>
|
|
861
|
+
|
|
862
|
+
<div>
|
|
863
|
+
<h4>Open Positions ({openPositions?.length ?? 0})</h4>
|
|
864
|
+
{openPositions?.map(pos => (
|
|
865
|
+
<div key={pos.positionId}>
|
|
866
|
+
PnL: ${pos.unrealizedPnl.toFixed(2)}
|
|
867
|
+
<button onClick={() => closePosition(pos.positionId, { slippage: 0.5 })}>
|
|
868
|
+
Close
|
|
869
|
+
</button>
|
|
870
|
+
</div>
|
|
871
|
+
))}
|
|
872
|
+
</div>
|
|
873
|
+
</div>
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
### Real-time Price Display
|
|
879
|
+
|
|
880
|
+
```tsx
|
|
881
|
+
import { useMarket, usePearHyperliquid } from '@pear-protocol/hyperliquid-sdk';
|
|
882
|
+
|
|
883
|
+
function PriceDisplay({ symbol }: { symbol: string }) {
|
|
884
|
+
const { isConnected, nativeIsConnected } = usePearHyperliquid();
|
|
885
|
+
const { getAssetByName } = useMarket();
|
|
886
|
+
|
|
887
|
+
const metadata = getAssetByName(symbol);
|
|
888
|
+
|
|
889
|
+
if (!nativeIsConnected) {
|
|
890
|
+
return <div>Connecting...</div>;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
return (
|
|
894
|
+
<div>
|
|
895
|
+
<h3>{symbol}</h3>
|
|
896
|
+
<p>Price: ${metadata?.currentPrice.toFixed(2)}</p>
|
|
897
|
+
<p>24h Change: {metadata?.priceChangePercent24h.toFixed(2)}%</p>
|
|
898
|
+
<p>Funding: {(metadata?.fundingRate * 100).toFixed(4)}%</p>
|
|
899
|
+
</div>
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
---
|
|
905
|
+
|
|
906
|
+
## Contributing
|
|
907
|
+
|
|
908
|
+
### Development Setup
|
|
909
|
+
|
|
910
|
+
1. Clone the repository:
|
|
911
|
+
|
|
912
|
+
```bash
|
|
913
|
+
git clone https://github.com/pear-protocol/hyperliquid.git
|
|
914
|
+
cd hyperliquid/pear-hyperliquid-sdk
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
2. Install dependencies:
|
|
918
|
+
|
|
919
|
+
```bash
|
|
920
|
+
yarn install
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
3. Start development mode:
|
|
924
|
+
|
|
925
|
+
```bash
|
|
926
|
+
yarn dev
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
### Project Structure
|
|
930
|
+
|
|
931
|
+
```
|
|
932
|
+
pear-hyperliquid-sdk/
|
|
933
|
+
├── src/
|
|
934
|
+
│ ├── clients/ # API client functions
|
|
935
|
+
│ │ ├── auth.ts # Authentication endpoints
|
|
936
|
+
│ │ ├── positions.ts # Position management
|
|
937
|
+
│ │ ├── orders.ts # Order management
|
|
938
|
+
│ │ ├── watchlist.ts # Watchlist operations
|
|
939
|
+
│ │ ├── hyperliquid.ts # Hyperliquid public API
|
|
940
|
+
│ │ └── ...
|
|
941
|
+
│ ├── hooks/ # React hooks
|
|
942
|
+
│ │ ├── useAuth.ts
|
|
943
|
+
│ │ ├── usePosition.ts
|
|
944
|
+
│ │ ├── useOrders.ts
|
|
945
|
+
│ │ ├── useMarket.ts
|
|
946
|
+
│ │ ├── useMarketData.ts
|
|
947
|
+
│ │ └── ... (20+ hooks)
|
|
948
|
+
│ ├── store/ # Zustand stores
|
|
949
|
+
│ │ ├── userDataStore.ts # User authentication & real-time data
|
|
950
|
+
│ │ ├── hyperliquidDataStore.ts # Market metadata & prices
|
|
951
|
+
│ │ ├── userSelection.ts # Token selection UI state
|
|
952
|
+
│ │ ├── marketDataStore.ts # Market baskets
|
|
953
|
+
│ │ └── ...
|
|
954
|
+
│ ├── utils/ # Utility functions
|
|
955
|
+
│ │ ├── position-validator.ts
|
|
956
|
+
│ │ ├── token-metadata-extractor.ts
|
|
957
|
+
│ │ ├── basket-calculator.ts
|
|
958
|
+
│ │ └── ...
|
|
959
|
+
│ ├── provider.tsx # PearHyperliquidProvider component
|
|
960
|
+
│ ├── websocket.ts # Pear API WebSocket
|
|
961
|
+
│ ├── hyperliquid-websocket.ts # Native Hyperliquid WebSocket
|
|
962
|
+
│ ├── types.ts # TypeScript definitions
|
|
963
|
+
│ └── index.ts # Public API exports
|
|
964
|
+
├── dist/ # Build output
|
|
965
|
+
├── package.json
|
|
966
|
+
├── tsconfig.json
|
|
967
|
+
└── rollup.config.js
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
### Architecture Overview
|
|
971
|
+
|
|
972
|
+
```
|
|
973
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
974
|
+
│ PearHyperliquidProvider │
|
|
975
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
976
|
+
│ ┌─────────────────────┐ ┌─────────────────────┐ │
|
|
977
|
+
│ │ Pear API WebSocket │ │ Hyperliquid Native │ │
|
|
978
|
+
│ │ (user data, orders)│ │ WS (market data) │ │
|
|
979
|
+
│ └──────────┬──────────┘ └──────────┬──────────┘ │
|
|
980
|
+
│ │ │ │
|
|
981
|
+
│ ▼ ▼ │
|
|
982
|
+
│ ┌─────────────────────────────────────────────────────────────┤
|
|
983
|
+
│ │ Zustand Stores │
|
|
984
|
+
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
985
|
+
│ │ │ userDataStore│ │hyperliquid │ │ userSelection│ │
|
|
986
|
+
│ │ │ (auth, pos) │ │ DataStore │ │ (UI state) │ │
|
|
987
|
+
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
988
|
+
│ └─────────────────────────────────────────────────────────────┤
|
|
989
|
+
│ │ │ │
|
|
990
|
+
│ ▼ ▼ │
|
|
991
|
+
│ ┌─────────────────────────────────────────────────────────────┤
|
|
992
|
+
│ │ React Hooks │
|
|
993
|
+
│ │ usePosition, useOrders, useMarket, useAuth, ... │
|
|
994
|
+
│ └─────────────────────────────────────────────────────────────┤
|
|
995
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
### Available Scripts
|
|
999
|
+
|
|
1000
|
+
| Script | Description |
|
|
1001
|
+
|--------|-------------|
|
|
1002
|
+
| `yarn build` | Build the SDK for production |
|
|
1003
|
+
| `yarn dev` | Start development mode with watch |
|
|
1004
|
+
| `yarn type-check` | Run TypeScript type checking |
|
|
1005
|
+
| `yarn clean` | Remove build artifacts |
|
|
1006
|
+
| `yarn copy-to-node-modules` | Copy build to parent node_modules (for local development) |
|
|
1007
|
+
|
|
1008
|
+
### Code Style
|
|
1009
|
+
|
|
1010
|
+
- **TypeScript**: Strict mode enabled
|
|
1011
|
+
- **Exports**: ESM only (no CommonJS)
|
|
1012
|
+
- **React**: Functional components with hooks
|
|
1013
|
+
- **State**: Zustand for global state
|
|
1014
|
+
- **API Calls**: Axios with interceptors
|
|
1015
|
+
|
|
1016
|
+
### Adding a New Hook
|
|
1017
|
+
|
|
1018
|
+
1. Create the hook file in `src/hooks/`:
|
|
1019
|
+
|
|
1020
|
+
```tsx
|
|
1021
|
+
// src/hooks/useMyFeature.ts
|
|
1022
|
+
import { useCallback } from 'react';
|
|
1023
|
+
import { useUserData } from '../store/userDataStore';
|
|
1024
|
+
import { usePearHyperliquid } from '../provider';
|
|
1025
|
+
|
|
1026
|
+
export function useMyFeature() {
|
|
1027
|
+
const { apiBaseUrl } = usePearHyperliquid();
|
|
1028
|
+
const address = useUserData((state) => state.address);
|
|
1029
|
+
|
|
1030
|
+
const myFunction = useCallback(async () => {
|
|
1031
|
+
// Implementation
|
|
1032
|
+
}, [apiBaseUrl, address]);
|
|
1033
|
+
|
|
1034
|
+
return { myFunction };
|
|
1035
|
+
}
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
2. Export from `src/hooks/index.ts`:
|
|
1039
|
+
|
|
1040
|
+
```tsx
|
|
1041
|
+
export * from './useMyFeature';
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
### Adding a New Client Function
|
|
1045
|
+
|
|
1046
|
+
1. Create or update the client file in `src/clients/`:
|
|
1047
|
+
|
|
1048
|
+
```tsx
|
|
1049
|
+
// src/clients/myFeature.ts
|
|
1050
|
+
import { apiClient } from '../utils/http';
|
|
1051
|
+
import type { ApiResponse } from '../types';
|
|
1052
|
+
|
|
1053
|
+
export interface MyRequestPayload {
|
|
1054
|
+
// ...
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
export interface MyResponseDto {
|
|
1058
|
+
// ...
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
export async function myApiCall(
|
|
1062
|
+
baseUrl: string,
|
|
1063
|
+
payload: MyRequestPayload
|
|
1064
|
+
): Promise<ApiResponse<MyResponseDto>> {
|
|
1065
|
+
const response = await apiClient.post(`${baseUrl}/my-endpoint`, payload);
|
|
1066
|
+
return response;
|
|
1067
|
+
}
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
2. Export from `src/index.ts`:
|
|
1071
|
+
|
|
1072
|
+
```tsx
|
|
1073
|
+
export * from './clients/myFeature';
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
### Adding New Types
|
|
1077
|
+
|
|
1078
|
+
Add types to `src/types.ts` and export them from `src/index.ts`:
|
|
1079
|
+
|
|
1080
|
+
```tsx
|
|
1081
|
+
// src/types.ts
|
|
1082
|
+
export interface MyNewType {
|
|
1083
|
+
// ...
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// src/index.ts
|
|
1087
|
+
export type { MyNewType } from './types';
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
### Building
|
|
1091
|
+
|
|
1092
|
+
```bash
|
|
1093
|
+
# Production build
|
|
1094
|
+
yarn build
|
|
1095
|
+
|
|
1096
|
+
# Type checking only
|
|
1097
|
+
yarn type-check
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
### Publishing
|
|
1101
|
+
|
|
1102
|
+
1. Update version in `package.json`
|
|
1103
|
+
2. Build the package: `yarn build`
|
|
1104
|
+
3. Publish to npm: `npm publish`
|
|
1105
|
+
|
|
1106
|
+
The package is published to npm as `@pear-protocol/hyperliquid-sdk`.
|
|
1107
|
+
|
|
1108
|
+
### Key Design Patterns
|
|
1109
|
+
|
|
1110
|
+
**Composition Pattern**: Multiple stores compose to create computed state. For example, `usePosition()` combines data from `userDataStore`, `hyperliquidDataStore`, and computes enriched position data.
|
|
1111
|
+
|
|
1112
|
+
**Selector Pattern**: Zustand selectors enable granular subscriptions. Only components using specific data re-render when it changes.
|
|
1113
|
+
|
|
1114
|
+
**WebSocket Multiplexing**: Two independent WebSocket streams handle different data types. The Pear API WebSocket handles user-specific data while the native Hyperliquid WebSocket handles market data.
|
|
1115
|
+
|
|
1116
|
+
**Lazy Initialization**: WebSocket connections wait for `perpMetaAssets` to be available before connecting, preventing premature subscriptions.
|
|
1117
|
+
|
|
1118
|
+
### Testing
|
|
1119
|
+
|
|
1120
|
+
When implementing tests:
|
|
1121
|
+
|
|
1122
|
+
1. Use React Testing Library for hook testing
|
|
1123
|
+
2. Mock WebSocket connections for real-time data tests
|
|
1124
|
+
3. Mock API responses with MSW or similar
|
|
1125
|
+
|
|
1126
|
+
### Commit Guidelines
|
|
1127
|
+
|
|
1128
|
+
- Use conventional commits format
|
|
1129
|
+
- Keep commits focused and atomic
|
|
1130
|
+
- Include relevant issue references
|
|
1131
|
+
|
|
1132
|
+
---
|
|
1133
|
+
|
|
1134
|
+
## License
|
|
1135
|
+
|
|
1136
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
1137
|
+
|
|
1138
|
+
## Support
|
|
1139
|
+
|
|
1140
|
+
- GitHub Issues: [github.com/pear-protocol/hyperliquid/issues](https://github.com/pear-protocol/hyperliquid/issues)
|
|
1141
|
+
- Documentation: [docs.pearprotocol.io](https://docs.pearprotocol.io)
|