Package Exports
- @pear-protocol/hyperliquid-sdk
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@pear-protocol/hyperliquid-sdk) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@pear-protocol/hyperliquid-sdk
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.
Table of Contents
- Installation
- Quick Start
- Provider Setup
- Authentication
- Hooks Reference
- Client Functions
- Utility Functions
- Types Reference
- Examples
- Contributing
Installation
npm install @pear-protocol/hyperliquid-sdk
# or
yarn add @pear-protocol/hyperliquid-sdk
# or
pnpm add @pear-protocol/hyperliquid-sdkPeer Dependencies
Ensure you have React 18+ installed:
npm install react react-domQuick Start
import {
PearHyperliquidProvider,
useAuth,
usePosition,
useMarket,
} from '@pear-protocol/hyperliquid-sdk';
function App() {
return (
<PearHyperliquidProvider
apiBaseUrl="https://hl-ui.pearprotocol.io"
wsUrl="wss://hl-ui.pearprotocol.io/ws"
clientId="YOUR_CLIENT_ID"
>
<TradingApp />
</PearHyperliquidProvider>
);
}
function TradingApp() {
const { isAuthenticated, loginWithSignedMessage } = useAuth();
const { openPositions, createPosition } = usePosition();
const { allTokenMetadata } = useMarket();
if (!isAuthenticated) {
return <button onClick={() => loginWithSignedMessage(address, signMessage)}>Connect</button>;
}
return (
<div>
<h2>Open Positions: {openPositions?.length ?? 0}</h2>
{/* Your trading UI */}
</div>
);
}Provider Setup
Wrap your application with PearHyperliquidProvider to enable SDK functionality:
import { PearHyperliquidProvider } from '@pear-protocol/hyperliquid-sdk';
function App() {
return (
<PearHyperliquidProvider
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}
</PearHyperliquidProvider>
);
}Provider Props
| 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 |
Accessing Provider Context
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:
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);
};
}Authentication Flow
- SDK requests an EIP-712 message from the server
- User signs the message with their wallet
- Signature is sent to authenticate
- JWT tokens are stored and managed automatically
Hooks Reference
Trading Hooks
usePosition()
Manage positions with full CRUD operations:
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:
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:
import { useOrders } from '@pear-protocol/hyperliquid-sdk';
function OrdersComponent() {
const {
openOrders, // OpenLimitOrderDto[]
triggerOrders, // Filtered trigger orders
adjustOrder,
cancelOrder,
} = useOrders();
}useSpotOrder()
Execute spot swaps:
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:
import { useTwap } from '@pear-protocol/hyperliquid-sdk';
function TwapMonitor() {
const { twapOrders, cancelTwapOrder } = useTwap();
return twapOrders?.map(order => (
<div key={order.orderId}>
Status: {order.status}
Progress: {order.filledUsdValue} / {order.totalUsdValue}
Chunks: {order.chunks.length - order.remainingChunks} / {order.chunks.length}
</div>
));
}Data Hooks
useMarket()
Access token metadata:
import { useMarket } from '@pear-protocol/hyperliquid-sdk';
function MarketData() {
const {
allTokenMetadata, // Record<string, TokenMetadata>
getAssetByName, // (symbol: string) => TokenMetadata | null
} = useMarket();
const btcData = getAssetByName('BTC');
// { symbol: 'BTC', currentPrice: 50000, maxLeverage: 50, ... }
}useMarketData()
Get market baskets and analytics:
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:
import { useAllUserBalances } from '@pear-protocol/hyperliquid-sdk';
function BalanceDisplay() {
const {
spotUsdcBalance,
spotUsdhBalance,
availableToTradeUsdc,
availableToTradeUsdh,
isLoading,
} = useAllUserBalances();
}useTokenSelectionMetadata()
Get computed metrics for selected tokens:
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:
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:
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,
});
}WebSocket Hooks
useHyperliquidWebSocket()
Access Pear API WebSocket (managed by provider):
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):
import { useHyperliquidNativeWebSocket } from '@pear-protocol/hyperliquid-sdk';
// Typically used internally by the provider
const { isConnected, lastError } = useHyperliquidNativeWebSocket({
address: userAddress,
tokens: ['BTC', 'ETH'],
enabled: true,
});UI State Hooks
useUserSelection()
Manage token selection state:
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:
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:
import { useNotifications } from '@pear-protocol/hyperliquid-sdk';
function NotificationCenter() {
const {
notifications, // NotificationDto[]
markAsRead,
isLoading,
} = useNotifications();
}useAccountSummary()
Get portfolio-level metrics:
import { useAccountSummary } from '@pear-protocol/hyperliquid-sdk';
function AccountOverview() {
const { accountSummary } = useAccountSummary();
return (
<div>
<p>Account Value: ${accountSummary?.totalAccountValue}</p>
<p>Unrealized PnL: ${accountSummary?.totalUnrealizedPnl}</p>
<p>Margin Used: ${accountSummary?.totalMarginUsed}</p>
</div>
);
}Client Functions
The SDK exports client functions for direct API calls:
Position Operations
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 immediately
await closePosition(apiBaseUrl, positionId, { executionType: 'MARKET' });
// Close position conditionally
await closePosition(apiBaseUrl, positionId, {
executionType: 'TRIGGER',
triggerType: 'WEIGHTED_RATIO',
triggerValue: '1.08',
direction: 'MORE_THAN',
});
// Update TP/SL
await updateRiskParameters(apiBaseUrl, positionId, {
takeProfit: { triggerType: 'WEIGHTED_RATIO', triggerValue: 1.1 },
stopLoss: { triggerType: 'WEIGHTED_RATIO', triggerValue: 0.95 },
});Order Operations
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,
});Watchlist Operations
import { toggleWatchlist } from '@pear-protocol/hyperliquid-sdk';
await toggleWatchlist(apiBaseUrl, longAssets, shortAssets);Utility Functions
Position Validation
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);Token Metadata
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');Basket Calculations
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);Chart Interval Conversion
import {
mapTradingViewIntervalToCandleInterval,
mapCandleIntervalToTradingViewInterval,
} from '@pear-protocol/hyperliquid-sdk';
const candleInterval = mapTradingViewIntervalToCandleInterval('60'); // '1h'
const tvInterval = mapCandleIntervalToTradingViewInterval('1h'); // '60'Order Helpers
import {
getOrderLeverage,
getOrderUsdValue,
getOrderTriggerType,
getOrderTriggerValue,
getOrderDirection,
isBtcDomOrder,
} from '@pear-protocol/hyperliquid-sdk';
const leverage = getOrderLeverage(order);
const isBtcDom = isBtcDomOrder(order);Conflict Detection
import { ConflictDetector } from '@pear-protocol/hyperliquid-sdk';
const conflicts = ConflictDetector.detectConflicts(longTokens, shortTokens);
// Returns TokenConflict[] if same token appears in both legsTypes Reference
Core Types
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';Key Type Definitions
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
Complete Trading Flow
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 (
<div>
<h2>Available: ${availableToTradeUsdc}</h2>
<h3>Weighted Ratio: {weightedRatio.toFixed(4)}</h3>
<h3>Max Leverage: {maxLeverage}x</h3>
<div>
<h4>Long Tokens</h4>
{longTokens.map((token, i) => (
<div key={i}>
{token.symbol}: {token.weight}%
@ ${allTokenMetadata[token.symbol]?.currentPrice}
</div>
))}
</div>
<button onClick={handleCreatePosition}>
Open Position
</button>
<div>
<h4>Open Positions ({openPositions?.length ?? 0})</h4>
{openPositions?.map(pos => (
<div key={pos.positionId}>
PnL: ${pos.unrealizedPnl.toFixed(2)}
<button onClick={() => closePosition(pos.positionId, { executionType: 'MARKET' })}>
Close
</button>
</div>
))}
</div>
</div>
);
}Real-time Price Display
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 <div>Connecting...</div>;
}
return (
<div>
<h3>{symbol}</h3>
<p>Price: ${metadata?.currentPrice.toFixed(2)}</p>
<p>24h Change: {metadata?.priceChangePercent24h.toFixed(2)}%</p>
<p>Funding: {(metadata?.fundingRate * 100).toFixed(4)}%</p>
</div>
);
}Contributing
Development Setup
- Clone the repository:
git clone https://github.com/pear-protocol/hyperliquid.git
cd hyperliquid/pear-hyperliquid-sdk- Install dependencies:
yarn install- Start development mode:
yarn devProject Structure
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.jsArchitecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ 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, ... │
│ └─────────────────────────────────────────────────────────────┤
└─────────────────────────────────────────────────────────────────┘Available Scripts
| 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) |
Code Style
- TypeScript: Strict mode enabled
- Exports: ESM only (no CommonJS)
- React: Functional components with hooks
- State: Zustand for global state
- API Calls: Axios with interceptors
Adding a New Hook
- Create the hook file in
src/hooks/:
// 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 };
}- Export from
src/hooks/index.ts:
export * from './useMyFeature';Adding a New Client Function
- Create or update the client file in
src/clients/:
// 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<ApiResponse<MyResponseDto>> {
const response = await apiClient.post(`${baseUrl}/my-endpoint`, payload);
return response;
}- Export from
src/index.ts:
export * from './clients/myFeature';Adding New Types
Add types to src/types.ts and export them from src/index.ts:
// src/types.ts
export interface MyNewType {
// ...
}
// src/index.ts
export type { MyNewType } from './types';Building
# Production build
yarn build
# Type checking only
yarn type-checkPublishing
- Update version in
package.json - Build the package:
yarn build - Publish to npm:
npm publish
The package is published to npm as @pear-protocol/hyperliquid-sdk.
Key Design Patterns
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.
Testing
When implementing tests:
- Use React Testing Library for hook testing
- Mock WebSocket connections for real-time data tests
- Mock API responses with MSW or similar
Commit Guidelines
- Use conventional commits format
- Keep commits focused and atomic
- Include relevant issue references
License
MIT License - see LICENSE for details.
Support
- GitHub Issues: github.com/pear-protocol/hyperliquid/issues
- Documentation: docs.pearprotocol.io