@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
npm install @pear-protocol/hyperliquid-sdk
yarn add @pear-protocol/hyperliquid-sdk
pnpm add @pear-protocol/hyperliquid-sdk
Peer Dependencies
Ensure you have React 18+ installed:
npm install react react-dom
Quick 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
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,
lastError,
nativeIsConnected,
nativeLastError,
} = 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 () => {
await loginWithSignedMessage(
walletAddress,
async (message) => {
return await signMessage({ message });
}
);
};
const handlePrivyLogin = async () => {
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,
isLoading,
createPosition,
closePosition,
closeAllPositions,
adjustPosition,
updateRiskParameters,
updateLeverage,
} = 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,
createTrade,
adjustTrade,
closeTrade,
} = useTrading();
}
useOrders()
Manage open and trigger orders:
import { useOrders } from '@pear-protocol/hyperliquid-sdk';
function OrdersComponent() {
const {
openOrders,
triggerOrders,
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,
getAssetByName,
} = useMarket();
const btcData = getAssetByName('BTC');
}
useMarketData()
Get market baskets and analytics:
import { useMarketData, type CollateralFilter } from '@pear-protocol/hyperliquid-sdk';
function MarketAnalytics() {
const {
activeBaskets,
highlightedBaskets,
topGainers,
topLosers,
watchlistBaskets,
isLoading,
collateralFilter,
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,
shortTokensMetadata,
weightedRatio,
weightedRatio24h,
priceRatio,
openInterest,
volume,
maxLeverage,
minMargin,
leverageMatched,
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,
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';
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';
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,
shortTokens,
addToken,
removeToken,
updateTokenWeight,
handleTokenSelect,
setTokenSelections,
resetToDefaults,
candleInterval,
setCandleInterval,
openTokenSelector,
setOpenTokenSelector,
conflicts,
openConflictModal,
} = useUserSelection();
const handleAddLong = () => {
const success = addToken(true);
};
}
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,
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';
const result = await createPosition(apiBaseUrl, {
longAssets: [{ asset: 'BTC', weight: 100 }],
shortAssets: [{ asset: 'ETH', weight: 100 }],
usdValue: 1000,
leverage: 5,
slippage: 0.5,
executionType: 'MARKET',
});
await closePosition(apiBaseUrl, positionId, { executionType: 'MARKET' });
await closePosition(apiBaseUrl, positionId, {
executionType: 'TRIGGER',
triggerType: 'WEIGHTED_RATIO',
triggerValue: '1.08',
direction: 'MORE_THAN',
});
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';
await adjustOrder(apiBaseUrl, orderId, { limitRatio: 1.05 });
await cancelOrder(apiBaseUrl, orderId);
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,
MAX_ASSETS_PER_LEG,
} 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';
const metadata = TokenMetadataExtractor.extractTokenMetadata(
'BTC',
perpMetaAssets,
assetContexts,
allMids,
activeAssetData
);
const filtered = selectTokenMetadataBySymbols(allMetadata, ['BTC', 'ETH']);
const btc = getAssetByName(allMetadata, 'BTC');
Basket Calculations
import {
computeBasketCandles,
calculateWeightedRatio,
createCandleLookups,
getCompleteTimestamps,
} from '@pear-protocol/hyperliquid-sdk';
const basketCandles = computeBasketCandles(
longAssets,
shortAssets,
longCandles,
shortCandles,
leverage
);
const ratio = calculateWeightedRatio(longMetadata, shortMetadata, weights);
Chart Interval Conversion
import {
mapTradingViewIntervalToCandleInterval,
mapCandleIntervalToTradingViewInterval,
} from '@pear-protocol/hyperliquid-sdk';
const candleInterval = mapTradingViewIntervalToCandleInterval('60');
const tvInterval = mapCandleIntervalToTradingViewInterval('1h');
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);
Types Reference
Core Types
import type {
ApiResponse,
ApiErrorResponse,
OpenPositionDto,
PositionAssetDetailDto,
OpenLimitOrderDto,
OrderAssetDto,
OrderStatus,
TriggerOrderNotificationType,
TokenMetadata,
ActiveAssetsResponse,
ActiveAssetGroupItem,
WebSocketConnectionState,
WebSocketChannel,
TokenSelection,
TokenConflict,
CandleInterval,
CandleData,
TwapMonitoringDto,
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
git clone https://github.com/pear-protocol/hyperliquid.git
cd hyperliquid/pear-hyperliquid-sdk
yarn install
yarn dev
Project 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.js
Architecture 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
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/:
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 () => {
}, [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/:
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:
export interface MyNewType {
}
export type { MyNewType } from './types';
Building
yarn build
yarn type-check
Publishing
- 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