React SDK for Pear Protocol Hyperliquid API integration
npm install @pear-protocol/hyperliquid-sdkA 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.
- Installation
- Quick Start
- Provider Setup
- Authentication
- Hooks Reference
- Trading Hooks
- Data Hooks
- WebSocket Hooks
- UI State Hooks
- Client Functions
- Utility Functions
- Types Reference
- Examples
- Contributing
---
``bash`
npm install @pear-protocol/hyperliquid-sdkor
yarn add @pear-protocol/hyperliquid-sdkor
pnpm add @pear-protocol/hyperliquid-sdk
Ensure you have React 18+ installed:
`bash`
npm install react react-dom
---
`tsx
import {
PearHyperliquidProvider,
useAuth,
usePosition,
useMarket,
} from '@pear-protocol/hyperliquid-sdk';
function App() {
return (
wsUrl="wss://hl-ui.pearprotocol.io/ws"
clientId="YOUR_CLIENT_ID"
>
);
}
function TradingApp() {
const { isAuthenticated, loginWithSignedMessage } = useAuth();
const { openPositions, createPosition } = usePosition();
const { allTokenMetadata } = useMarket();
if (!isAuthenticated) {
return ;
}
return (
---
Provider Setup
Wrap your application with
PearHyperliquidProvider to enable SDK functionality:`tsx
import { PearHyperliquidProvider } from '@pear-protocol/hyperliquid-sdk';function App() {
return (
apiBaseUrl="https://hl-ui.pearprotocol.io" // Pear API endpoint
wsUrl="wss://hl-ui.pearprotocol.io/ws" // WebSocket endpoint
clientId="YOUR_CLIENT_ID" // Your application ID
>
{children}
);
}
`$3
| Prop | Type | Default | Description |
|------|------|---------|-------------|
|
apiBaseUrl | string | 'https://hl-ui.pearprotocol.io' | Pear Protocol API base URL |
| wsUrl | string | 'wss://hl-ui.pearprotocol.io/ws' | WebSocket server URL |
| clientId | string | 'PEARPROTOCOLUI' | Application client identifier |$3
`tsx
import { usePearHyperliquid } from '@pear-protocol/hyperliquid-sdk';function Component() {
const {
clientId,
apiBaseUrl,
wsUrl,
isConnected, // Pear WebSocket connection status
lastError, // Pear WebSocket last error
nativeIsConnected, // Hyperliquid WebSocket connection status
nativeLastError, // Hyperliquid WebSocket last error
} = usePearHyperliquid();
}
`---
Authentication
The SDK provides EIP-712 signature-based authentication:
`tsx
import { useAuth } from '@pear-protocol/hyperliquid-sdk';function AuthComponent() {
const {
isAuthenticated,
isLoading,
address,
loginWithSignedMessage,
loginWithPrivy,
logout,
} = useAuth();
const handleLogin = async () => {
// Using wallet signature (e.g., with wagmi/viem)
await loginWithSignedMessage(
walletAddress,
async (message) => {
// Your wallet's signMessage function
return await signMessage({ message });
}
);
};
const handlePrivyLogin = async () => {
// Using Privy authentication
await loginWithPrivy(walletAddress, privyAccessToken);
};
}
`$3
1. SDK requests an EIP-712 message from the server
2. User signs the message with their wallet
3. Signature is sent to authenticate
4. JWT tokens are stored and managed automatically
---
Hooks Reference
$3
####
usePosition()Manage positions with full CRUD operations:
`tsx
import { usePosition } from '@pear-protocol/hyperliquid-sdk';function PositionManager() {
const {
openPositions, // OpenPositionDto[] - enriched position data
isLoading,
createPosition, // Create new position
closePosition, // Close specific position
closeAllPositions, // Close all positions
adjustPosition, // Rebalance position weights
updateRiskParameters, // Update TP/SL
updateLeverage, // Change leverage
} = usePosition();
const handleCreate = async () => {
await createPosition({
longAssets: [{ asset: 'BTC', weight: 50 }, { asset: 'ETH', weight: 50 }],
shortAssets: [{ asset: 'SOL', weight: 100 }],
usdValue: 1000,
leverage: 5,
slippage: 0.5,
executionType: 'MARKET',
takeProfit: { triggerType: 'WEIGHTED_RATIO', triggerValue: 1.1 },
stopLoss: { triggerType: 'WEIGHTED_RATIO', triggerValue: 0.95 },
});
};
}
`####
useTrading()High-level trading operations with enriched trade history:
`tsx
import { useTrading } from '@pear-protocol/hyperliquid-sdk';function TradingComponent() {
const {
tradeHistories, // Trade history with metadata
createTrade,
adjustTrade,
closeTrade,
} = useTrading();
}
`####
useOrders()Manage open and trigger orders:
`tsx
import { useOrders } from '@pear-protocol/hyperliquid-sdk';function OrdersComponent() {
const {
openOrders, // OpenLimitOrderDto[]
triggerOrders, // Filtered trigger orders
adjustOrder,
cancelOrder,
} = useOrders();
}
`####
useSpotOrder()Execute spot swaps:
`tsx
import { useSpotOrder } from '@pear-protocol/hyperliquid-sdk';function SpotSwap() {
const { executeSpotOrder, isLoading } = useSpotOrder();
const handleSwap = async () => {
await executeSpotOrder({
fromAsset: 'USDC',
toAsset: 'USDH',
amount: 100,
});
};
}
`####
useTwap()Monitor TWAP order execution:
`tsx
import { useTwap } from '@pear-protocol/hyperliquid-sdk';function TwapMonitor() {
const { twapOrders, cancelTwapOrder } = useTwap();
return twapOrders?.map(order => (
Status: {order.status}
Progress: {order.filledUsdValue} / {order.totalUsdValue}
Chunks: {order.chunks.length - order.remainingChunks} / {order.chunks.length}
));
}
`$3
####
useMarket()Access token metadata:
`tsx
import { useMarket } from '@pear-protocol/hyperliquid-sdk';function MarketData() {
const {
allTokenMetadata, // Record
getAssetByName, // (symbol: string) => TokenMetadata | null
} = useMarket();
const btcData = getAssetByName('BTC');
// { symbol: 'BTC', currentPrice: 50000, maxLeverage: 50, ... }
}
`####
useMarketData()Get market baskets and analytics:
`tsx
import { useMarketData, type CollateralFilter } from '@pear-protocol/hyperliquid-sdk';function MarketAnalytics() {
const {
activeBaskets, // Active market baskets
highlightedBaskets, // Featured baskets
topGainers, // Best performing
topLosers, // Worst performing
watchlistBaskets, // User's watchlist
isLoading,
collateralFilter, // 'ALL' | 'USDC' | 'USDH'
setCollateralFilter,
} = useMarketData();
}
`####
useAllUserBalances()Aggregate user balances:
`tsx
import { useAllUserBalances } from '@pear-protocol/hyperliquid-sdk';function BalanceDisplay() {
const {
spotUsdcBalance,
spotUsdhBalance,
availableToTradeUsdc,
availableToTradeUsdh,
isLoading,
} = useAllUserBalances();
}
`####
useTokenSelectionMetadata()Get computed metrics for selected tokens:
`tsx
import { useTokenSelectionMetadata } from '@pear-protocol/hyperliquid-sdk';function SelectionMetrics() {
const {
longTokensMetadata, // Metadata for selected long tokens
shortTokensMetadata, // Metadata for selected short tokens
weightedRatio, // Current weighted price ratio
weightedRatio24h, // 24h weighted ratio change
priceRatio, // Simple price ratio
openInterest, // Combined OI
volume, // Combined volume
maxLeverage, // Maximum allowed leverage
minMargin, // Minimum margin required
leverageMatched, // If all tokens support same leverage
isPriceDataReady,
} = useTokenSelectionMetadata();
}
`####
useHistoricalPriceData()Fetch and cache historical prices:
`tsx
import { useHistoricalPriceData } from '@pear-protocol/hyperliquid-sdk';function ChartData() {
const { fetchHistoricalData, getCachedData } = useHistoricalPriceData();
useEffect(() => {
fetchHistoricalData('BTC', startTime, endTime, '1h');
}, []);
const candles = getCachedData('BTC', startTime, endTime);
}
`####
useBasketCandles()Compose weighted candles from multiple assets:
`tsx
import { useBasketCandles } from '@pear-protocol/hyperliquid-sdk';function BasketChart() {
const {
basketCandles, // Weighted OHLCV data
isLoading,
error,
} = useBasketCandles({
longAssets: [{ symbol: 'BTC', weight: 50 }, { symbol: 'ETH', weight: 50 }],
shortAssets: [{ symbol: 'SOL', weight: 100 }],
interval: '1h',
leverage: 5,
});
}
`$3
####
useHyperliquidWebSocket()Access Pear API WebSocket (managed by provider):
`tsx
import { useHyperliquidWebSocket } from '@pear-protocol/hyperliquid-sdk';// Typically used internally by the provider
const { isConnected, lastError } = useHyperliquidWebSocket({
wsUrl: 'wss://hl-ui.pearprotocol.io/ws',
address: userAddress,
enabled: true,
});
`####
useHyperliquidNativeWebSocket()Access Hyperliquid native WebSocket (managed by provider):
`tsx
import { useHyperliquidNativeWebSocket } from '@pear-protocol/hyperliquid-sdk';// Typically used internally by the provider
const { isConnected, lastError } = useHyperliquidNativeWebSocket({
address: userAddress,
tokens: ['BTC', 'ETH'],
enabled: true,
});
`$3
####
useUserSelection()Manage token selection state:
`tsx
import { useUserSelection } from '@pear-protocol/hyperliquid-sdk';function TokenSelector() {
const {
longTokens, // TokenSelection[]
shortTokens, // TokenSelection[]
addToken, // (isLong: boolean) => boolean
removeToken, // (isLong: boolean, index: number) => void
updateTokenWeight, // (isLong: boolean, index: number, weight: number) => void
handleTokenSelect, // (symbol: string) => void
setTokenSelections, // (longs, shorts) => void
resetToDefaults, // Reset to default selection
candleInterval, // Current chart interval
setCandleInterval, // Set chart interval
openTokenSelector, // Token selector modal state
setOpenTokenSelector,
conflicts, // Detected token conflicts
openConflictModal,
} = useUserSelection();
const handleAddLong = () => {
const success = addToken(true); // Returns false if MAX_ASSETS_PER_LEG reached
};
}
`####
useWatchlist()Manage user watchlist:
`tsx
import { useWatchlist } from '@pear-protocol/hyperliquid-sdk';function Watchlist() {
const { toggleWatchlist, isLoading } = useWatchlist();
const handleToggle = async () => {
await toggleWatchlist(
[{ asset: 'BTC', weight: 50 }],
[{ asset: 'ETH', weight: 50 }]
);
};
}
`####
useNotifications()Access user notifications:
`tsx
import { useNotifications } from '@pear-protocol/hyperliquid-sdk';function NotificationCenter() {
const {
notifications, // NotificationDto[]
markAsRead,
isLoading,
} = useNotifications();
}
`####
useAccountSummary()Get portfolio-level metrics:
`tsx
import { useAccountSummary } from '@pear-protocol/hyperliquid-sdk';function AccountOverview() {
const { accountSummary } = useAccountSummary();
return (
Account Value: ${accountSummary?.totalAccountValue}
Unrealized PnL: ${accountSummary?.totalUnrealizedPnl}
Margin Used: ${accountSummary?.totalMarginUsed}
);
}
`---
Client Functions
The SDK exports client functions for direct API calls:
$3
`tsx
import {
createPosition,
closePosition,
closeAllPositions,
adjustPosition,
updateRiskParameters,
updateLeverage,
} from '@pear-protocol/hyperliquid-sdk';// Create position
const result = await createPosition(apiBaseUrl, {
longAssets: [{ asset: 'BTC', weight: 100 }],
shortAssets: [{ asset: 'ETH', weight: 100 }],
usdValue: 1000,
leverage: 5,
slippage: 0.5,
executionType: 'MARKET',
});
// Close position
await closePosition(apiBaseUrl, positionId, { slippage: 0.5 });
// Update TP/SL
await updateRiskParameters(apiBaseUrl, positionId, {
takeProfit: { triggerType: 'WEIGHTED_RATIO', triggerValue: 1.1 },
stopLoss: { triggerType: 'WEIGHTED_RATIO', triggerValue: 0.95 },
});
`$3
`tsx
import {
adjustOrder,
cancelOrder,
cancelTwapOrder,
executeSpotOrder,
} from '@pear-protocol/hyperliquid-sdk';// Adjust limit order
await adjustOrder(apiBaseUrl, orderId, { limitRatio: 1.05 });
// Cancel order
await cancelOrder(apiBaseUrl, orderId);
// Execute spot swap
await executeSpotOrder(apiBaseUrl, {
fromAsset: 'USDC',
toAsset: 'USDH',
amount: 100,
});
`$3
`tsx
import { toggleWatchlist } from '@pear-protocol/hyperliquid-sdk';await toggleWatchlist(apiBaseUrl, longAssets, shortAssets);
`---
Utility Functions
$3
`tsx
import {
validateMinimumAssetSize,
validateMaxAssetsPerLeg,
calculateMinimumPositionValue,
MINIMUM_ASSET_USD_VALUE, // 11
MAX_ASSETS_PER_LEG, // 15
} from '@pear-protocol/hyperliquid-sdk';try {
validateMinimumAssetSize(usdValue, longAssets, shortAssets);
validateMaxAssetsPerLeg(longAssets, shortAssets);
} catch (error) {
if (error instanceof MinimumPositionSizeError) {
console.log('Minimum:', error.minimumRequired);
}
}
const minValue = calculateMinimumPositionValue(longAssets, shortAssets);
`$3
`tsx
import {
TokenMetadataExtractor,
selectTokenMetadataBySymbols,
getAssetByName,
} from '@pear-protocol/hyperliquid-sdk';// Extract metadata for a token
const metadata = TokenMetadataExtractor.extractTokenMetadata(
'BTC',
perpMetaAssets,
assetContexts,
allMids,
activeAssetData
);
// Filter metadata by symbols
const filtered = selectTokenMetadataBySymbols(allMetadata, ['BTC', 'ETH']);
// Single lookup
const btc = getAssetByName(allMetadata, 'BTC');
`$3
`tsx
import {
computeBasketCandles,
calculateWeightedRatio,
createCandleLookups,
getCompleteTimestamps,
} from '@pear-protocol/hyperliquid-sdk';// Compute weighted basket candles
const basketCandles = computeBasketCandles(
longAssets,
shortAssets,
longCandles,
shortCandles,
leverage
);
// Calculate weighted price ratio
const ratio = calculateWeightedRatio(longMetadata, shortMetadata, weights);
`$3
`tsx
import {
mapTradingViewIntervalToCandleInterval,
mapCandleIntervalToTradingViewInterval,
} from '@pear-protocol/hyperliquid-sdk';const candleInterval = mapTradingViewIntervalToCandleInterval('60'); // '1h'
const tvInterval = mapCandleIntervalToTradingViewInterval('1h'); // '60'
`$3
`tsx
import {
getOrderLeverage,
getOrderUsdValue,
getOrderTriggerType,
getOrderTriggerValue,
getOrderDirection,
isBtcDomOrder,
} from '@pear-protocol/hyperliquid-sdk';const leverage = getOrderLeverage(order);
const isBtcDom = isBtcDomOrder(order);
`$3
`tsx
import { ConflictDetector } from '@pear-protocol/hyperliquid-sdk';const conflicts = ConflictDetector.detectConflicts(longTokens, shortTokens);
// Returns TokenConflict[] if same token appears in both legs
`---
Types Reference
$3
`tsx
import type {
// API Response
ApiResponse,
ApiErrorResponse, // Position Types
OpenPositionDto,
PositionAssetDetailDto,
// Order Types
OpenLimitOrderDto,
OrderAssetDto,
OrderStatus,
TriggerOrderNotificationType,
// Market Data
TokenMetadata,
ActiveAssetsResponse,
ActiveAssetGroupItem,
// WebSocket
WebSocketConnectionState,
WebSocketChannel,
// Token Selection
TokenSelection,
TokenConflict,
// Candles
CandleInterval,
CandleData,
// TWAP
TwapMonitoringDto,
// Account
PlatformAccountSummaryResponseDto,
} from '@pear-protocol/hyperliquid-sdk';
`$3
`tsx
interface TokenMetadata {
symbol: string;
assetName: string;
currentPrice: number;
priceChange24h: number;
priceChangePercent24h: number;
maxLeverage: number;
openInterest: string;
volume: string;
fundingRate: number;
isAtOiCaps: boolean;
collateralToken?: 'USDC' | 'USDH';
}interface OpenPositionDto {
positionId: string;
longAssets: PositionAssetDetailDto[];
shortAssets: PositionAssetDetailDto[];
entryRatio: number;
markRatio: number;
marginUsed: number;
positionValue: number;
unrealizedPnl: number;
takeProfit?: TpSlOrderParameters;
stopLoss?: TpSlOrderParameters;
}
type TriggerOrderNotificationType =
| 'PRICE'
| 'PRICE_RATIO'
| 'WEIGHTED_RATIO'
| 'CROSS_ASSET_PRICE'
| 'PREDICTION_MARKET_OUTCOME'
| 'BTC_DOM';
type CandleInterval = '1m' | '5m' | '15m' | '1h' | '4h' | '1d';
`---
Examples
$3
`tsx
import {
PearHyperliquidProvider,
useAuth,
usePosition,
useMarket,
useUserSelection,
useTokenSelectionMetadata,
useAllUserBalances,
} from '@pear-protocol/hyperliquid-sdk';function TradingInterface() {
const { isAuthenticated, loginWithSignedMessage } = useAuth();
const { openPositions, createPosition, closePosition } = usePosition();
const { allTokenMetadata } = useMarket();
const { longTokens, shortTokens, addToken, updateTokenWeight } = useUserSelection();
const { weightedRatio, maxLeverage, minMargin } = useTokenSelectionMetadata();
const { availableToTradeUsdc } = useAllUserBalances();
const handleCreatePosition = async () => {
try {
await createPosition({
longAssets: longTokens.map(t => ({ asset: t.symbol, weight: t.weight })),
shortAssets: shortTokens.map(t => ({ asset: t.symbol, weight: t.weight })),
usdValue: 1000,
leverage: Math.min(maxLeverage, 10),
slippage: 0.5,
executionType: 'MARKET',
});
} catch (error) {
console.error('Failed to create position:', error);
}
};
return (
Available: ${availableToTradeUsdc}
Weighted Ratio: {weightedRatio.toFixed(4)}
Max Leverage: {maxLeverage}x
Long Tokens
{longTokens.map((token, i) => (
{token.symbol}: {token.weight}%
@ ${allTokenMetadata[token.symbol]?.currentPrice}
))}
Open Positions ({openPositions?.length ?? 0})
{openPositions?.map(pos => (
PnL: ${pos.unrealizedPnl.toFixed(2)}
))}
);
}
`$3
`tsx
import { useMarket, usePearHyperliquid } from '@pear-protocol/hyperliquid-sdk';function PriceDisplay({ symbol }: { symbol: string }) {
const { isConnected, nativeIsConnected } = usePearHyperliquid();
const { getAssetByName } = useMarket();
const metadata = getAssetByName(symbol);
if (!nativeIsConnected) {
return
Connecting...;
} return (
{symbol}
Price: ${metadata?.currentPrice.toFixed(2)}
24h Change: {metadata?.priceChangePercent24h.toFixed(2)}%
Funding: {(metadata?.fundingRate * 100).toFixed(4)}%
);
}
`---
Contributing
$3
1. Clone the repository:
`bash
git clone https://github.com/pear-protocol/hyperliquid.git
cd hyperliquid/pear-hyperliquid-sdk
`2. Install dependencies:
`bash
yarn install
`3. Start development mode:
`bash
yarn dev
`$3
`
pear-hyperliquid-sdk/
├── src/
│ ├── clients/ # API client functions
│ │ ├── auth.ts # Authentication endpoints
│ │ ├── positions.ts # Position management
│ │ ├── orders.ts # Order management
│ │ ├── watchlist.ts # Watchlist operations
│ │ ├── hyperliquid.ts # Hyperliquid public API
│ │ └── ...
│ ├── hooks/ # React hooks
│ │ ├── useAuth.ts
│ │ ├── usePosition.ts
│ │ ├── useOrders.ts
│ │ ├── useMarket.ts
│ │ ├── useMarketData.ts
│ │ └── ... (20+ hooks)
│ ├── store/ # Zustand stores
│ │ ├── userDataStore.ts # User authentication & real-time data
│ │ ├── hyperliquidDataStore.ts # Market metadata & prices
│ │ ├── userSelection.ts # Token selection UI state
│ │ ├── marketDataStore.ts # Market baskets
│ │ └── ...
│ ├── utils/ # Utility functions
│ │ ├── position-validator.ts
│ │ ├── token-metadata-extractor.ts
│ │ ├── basket-calculator.ts
│ │ └── ...
│ ├── provider.tsx # PearHyperliquidProvider component
│ ├── websocket.ts # Pear API WebSocket
│ ├── hyperliquid-websocket.ts # Native Hyperliquid WebSocket
│ ├── types.ts # TypeScript definitions
│ └── index.ts # Public API exports
├── dist/ # Build output
├── package.json
├── tsconfig.json
└── rollup.config.js
`$3
`
┌─────────────────────────────────────────────────────────────────┐
│ PearHyperliquidProvider │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Pear API WebSocket │ │ Hyperliquid Native │ │
│ │ (user data, orders)│ │ WS (market data) │ │
│ └──────────┬──────────┘ └──────────┬──────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┤
│ │ Zustand Stores │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ │ userDataStore│ │hyperliquid │ │ userSelection│ │
│ │ │ (auth, pos) │ │ DataStore │ │ (UI state) │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │
│ └─────────────────────────────────────────────────────────────┤
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┤
│ │ React Hooks │
│ │ usePosition, useOrders, useMarket, useAuth, ... │
│ └─────────────────────────────────────────────────────────────┤
└─────────────────────────────────────────────────────────────────┘
`$3
| Script | Description |
|--------|-------------|
|
yarn build | Build the SDK for production |
| yarn dev | Start development mode with watch |
| yarn type-check | Run TypeScript type checking |
| yarn clean | Remove build artifacts |
| yarn copy-to-node-modules | Copy build to parent node_modules (for local development) |$3
- TypeScript: Strict mode enabled
- Exports: ESM only (no CommonJS)
- React: Functional components with hooks
- State: Zustand for global state
- API Calls: Axios with interceptors
$3
1. Create the hook file in
src/hooks/:`tsx
// src/hooks/useMyFeature.ts
import { useCallback } from 'react';
import { useUserData } from '../store/userDataStore';
import { usePearHyperliquid } from '../provider';export function useMyFeature() {
const { apiBaseUrl } = usePearHyperliquid();
const address = useUserData((state) => state.address);
const myFunction = useCallback(async () => {
// Implementation
}, [apiBaseUrl, address]);
return { myFunction };
}
`2. Export from
src/hooks/index.ts:`tsx
export * from './useMyFeature';
`$3
1. Create or update the client file in
src/clients/:`tsx
// src/clients/myFeature.ts
import { apiClient } from '../utils/http';
import type { ApiResponse } from '../types';export interface MyRequestPayload {
// ...
}
export interface MyResponseDto {
// ...
}
export async function myApiCall(
baseUrl: string,
payload: MyRequestPayload
): Promise> {
const response = await apiClient.post(
${baseUrl}/my-endpoint, payload);
return response;
}
`2. Export from
src/index.ts:`tsx
export * from './clients/myFeature';
`$3
Add types to
src/types.ts and export them from src/index.ts:`tsx
// src/types.ts
export interface MyNewType {
// ...
}// src/index.ts
export type { MyNewType } from './types';
`$3
`bash
Production build
yarn buildType checking only
yarn type-check
`$3
1. Update version in
package.json
2. Build the package: yarn build
3. Publish to npm: npm publishThe package is published to npm as
@pear-protocol/hyperliquid-sdk.$3
Composition Pattern: Multiple stores compose to create computed state. For example,
usePosition() combines data from userDataStore, hyperliquidDataStore, and computes enriched position data.Selector Pattern: Zustand selectors enable granular subscriptions. Only components using specific data re-render when it changes.
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.
Lazy Initialization: WebSocket connections wait for
perpMetaAssets` to be available before connecting, preventing premature subscriptions.When implementing tests:
1. Use React Testing Library for hook testing
2. Mock WebSocket connections for real-time data tests
3. Mock API responses with MSW or similar
- Use conventional commits format
- Keep commits focused and atomic
- Include relevant issue references
---
MIT License - see LICENSE for details.
- GitHub Issues: github.com/pear-protocol/hyperliquid/issues
- Documentation: docs.pearprotocol.io