npm package: @pear-protocol/hyperliquid-sdk
Description: React SDK for Pear Protocol Hyperliquid API integration
Author: Pear Protocol
Latest version: 0.1.21

# @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](#installation)
- [Quick Start](#quick-start)
- [Provider Setup](#provider-setup)
- [Authentication](#authentication)
- [Hooks Reference](#hooks-reference)
  - [Trading Hooks](#trading-hooks)
  - [Data Hooks](#data-hooks)
  - [WebSocket Hooks](#websocket-hooks)
  - [UI State Hooks](#ui-state-hooks)
- [Client Functions](#client-functions)
- [Utility Functions](#utility-functions)
- [Types Reference](#types-reference)
- [Examples](#examples)
- [Contributing](#contributing)

---

## Installation

```bash
npm install @pear-protocol/hyperliquid-sdk
# or
yarn add @pear-protocol/hyperliquid-sdk
# or
pnpm add @pear-protocol/hyperliquid-sdk
```

### Peer Dependencies

Ensure you have React 18+ installed:

```bash
npm install react react-dom
```

---

## Quick Start

```tsx
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:

```tsx
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

```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);
  };
}
```

### Authentication Flow

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

### Trading Hooks

#### `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 => (
    <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:

```tsx
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:

```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,
  });
}
```

### WebSocket Hooks

#### `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,
});
```

### UI State Hooks

#### `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 (
    <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

```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 immediately
await closePosition(apiBaseUrl, positionId, { executionType: 'MARKET' });

// Close position conditionally
// Example: close when the weighted price ratio rises above 1.08.
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

```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,
});
```

### Watchlist Operations

```tsx
import { toggleWatchlist } from '@pear-protocol/hyperliquid-sdk';

await toggleWatchlist(apiBaseUrl, longAssets, shortAssets);
```

---

## Utility Functions

### Position Validation

```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);
```

### Token Metadata

```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');
```

### Basket Calculations

```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);
```

### Chart Interval Conversion

```tsx
import {
  mapTradingViewIntervalToCandleInterval,
  mapCandleIntervalToTradingViewInterval,
} from '@pear-protocol/hyperliquid-sdk';

const candleInterval = mapTradingViewIntervalToCandleInterval('60'); // '1h'
const tvInterval = mapCandleIntervalToTradingViewInterval('1h'); // '60'
```

### Order Helpers

```tsx
import {
  getOrderLeverage,
  getOrderUsdValue,
  getOrderTriggerType,
  getOrderTriggerValue,
  getOrderDirection,
  isBtcDomOrder,
} from '@pear-protocol/hyperliquid-sdk';

const leverage = getOrderLeverage(order);
const isBtcDom = isBtcDomOrder(order);
```

### Conflict Detection

```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

### Core Types

```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';
```

### Key Type Definitions

```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

### Complete Trading Flow

```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 (
    <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

```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 <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

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
```

### 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

| 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

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';
```

### Adding a New Client Function

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<ApiResponse<MyResponseDto>> {
  const response = await apiClient.post(`${baseUrl}/my-endpoint`, payload);
  return response;
}
```

2. Export from `src/index.ts`:

```tsx
export * from './clients/myFeature';
```

### Adding New Types

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';
```

### Building

```bash
# Production build
yarn build

# Type checking only
yarn type-check
```

### Publishing

1. Update version in `package.json`
2. Build the package: `yarn build`
3. 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:

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

### Commit Guidelines

- Use conventional commits format
- Keep commits focused and atomic
- Include relevant issue references

---

## License

MIT License - see [LICENSE](LICENSE) for details.

## Support

- GitHub Issues: [github.com/pear-protocol/hyperliquid/issues](https://github.com/pear-protocol/hyperliquid/issues)
- Documentation: [docs.pearprotocol.io](https://docs.pearprotocol.io)