@pear-protocol/hyperliquid-sdk 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1513,7 +1513,36 @@ const useOpenOrders = () => {
1513
1513
  };
1514
1514
 
1515
1515
  const useUserSelection = () => {
1516
- return useUserSelection$1();
1516
+ const longTokens = useUserSelection$1((s) => s.longTokens);
1517
+ const shortTokens = useUserSelection$1((s) => s.shortTokens);
1518
+ const candleInterval = useUserSelection$1((s) => s.candleInterval);
1519
+ const openTokenSelector = useUserSelection$1((s) => s.openTokenSelector);
1520
+ const selectorConfig = useUserSelection$1((s) => s.selectorConfig);
1521
+ const openConflictModal = useUserSelection$1((s) => s.openConflictModal);
1522
+ const setLongTokens = useUserSelection$1((s) => s.setLongTokens);
1523
+ const setShortTokens = useUserSelection$1((s) => s.setShortTokens);
1524
+ const setCandleInterval = useUserSelection$1((s) => s.setCandleInterval);
1525
+ const setTokenSelections = useUserSelection$1((s) => s.setTokenSelections);
1526
+ const setOpenTokenSelector = useUserSelection$1((s) => s.setOpenTokenSelector);
1527
+ const setSelectorConfig = useUserSelection$1((s) => s.setSelectorConfig);
1528
+ const setOpenConflictModal = useUserSelection$1((s) => s.setOpenConflictModal);
1529
+ const addToken = useUserSelection$1((s) => s.addToken);
1530
+ return {
1531
+ longTokens,
1532
+ shortTokens,
1533
+ candleInterval,
1534
+ openTokenSelector,
1535
+ selectorConfig,
1536
+ openConflictModal,
1537
+ setLongTokens,
1538
+ setShortTokens,
1539
+ setCandleInterval,
1540
+ setTokenSelections,
1541
+ setOpenTokenSelector,
1542
+ setSelectorConfig,
1543
+ setOpenConflictModal,
1544
+ addToken,
1545
+ };
1517
1546
  };
1518
1547
 
1519
1548
  const useTokenSelectionMetadataStore = create((set) => ({
@@ -1529,11 +1558,10 @@ const useTokenSelectionMetadataStore = create((set) => ({
1529
1558
  volume: "0",
1530
1559
  sumNetFunding: 0,
1531
1560
  maxLeverage: 0,
1532
- minMargin: 0,
1561
+ minSize: {},
1533
1562
  leverageMatched: true,
1534
- recompute: ({ perpMetaAssets, tokenMetadata, marketData, longTokens, shortTokens, }) => {
1563
+ recompute: ({ tokenMetadata, marketData, longTokens, shortTokens, }) => {
1535
1564
  const isPriceDataReady = Object.keys(tokenMetadata).length > 0;
1536
- // Get token symbols for lookups
1537
1565
  const longSymbols = longTokens.map((t) => t.symbol);
1538
1566
  const shortSymbols = shortTokens.map((t) => t.symbol);
1539
1567
  // Get metadata
@@ -1656,24 +1684,37 @@ const useTokenSelectionMetadataStore = create((set) => ({
1656
1684
  })();
1657
1685
  // Max leverage (maximum across all tokens)
1658
1686
  const maxLeverage = (() => {
1659
- if (!perpMetaAssets)
1687
+ if (!tokenMetadata)
1660
1688
  return 0;
1661
1689
  const allSymbols = [...longSymbols, ...shortSymbols];
1662
1690
  if (allSymbols.length === 0)
1663
1691
  return 0;
1664
1692
  let maxLev = 0;
1665
1693
  allSymbols.forEach((symbol) => {
1666
- const tokenUniverse = perpMetaAssets.find((u) => u.name === symbol);
1667
- if (tokenUniverse === null || tokenUniverse === void 0 ? void 0 : tokenUniverse.maxLeverage) {
1668
- maxLev = Math.max(maxLev, tokenUniverse.maxLeverage);
1694
+ var _a;
1695
+ const tokenDetail = tokenMetadata[symbol];
1696
+ if ((_a = tokenDetail === null || tokenDetail === void 0 ? void 0 : tokenDetail.leverage) === null || _a === void 0 ? void 0 : _a.value) {
1697
+ maxLev = Math.max(maxLev, tokenDetail.leverage.value);
1669
1698
  }
1670
1699
  });
1671
1700
  return maxLev;
1672
1701
  })();
1673
- // Min margin (10 * total number of tokens)
1674
- const minMargin = (() => {
1675
- const totalTokenCount = longTokens.length + shortTokens.length;
1676
- return 10 * totalTokenCount;
1702
+ const minSize = (() => {
1703
+ const allSymbols = [...longSymbols, ...shortSymbols];
1704
+ const collateralCounts = {};
1705
+ allSymbols.forEach((symbol) => {
1706
+ var _a;
1707
+ const collateralToken = (_a = tokenMetadata[symbol]) === null || _a === void 0 ? void 0 : _a.collateralToken;
1708
+ if (collateralToken) {
1709
+ collateralCounts[collateralToken] =
1710
+ (collateralCounts[collateralToken] || 0) + 1;
1711
+ }
1712
+ });
1713
+ const minSizeMap = {};
1714
+ Object.entries(collateralCounts).forEach(([collateral, count]) => {
1715
+ minSizeMap[collateral] = 11 * count;
1716
+ });
1717
+ return minSizeMap;
1677
1718
  })();
1678
1719
  // Whether all tokens have matching leverage
1679
1720
  const leverageMatched = (() => {
@@ -1708,7 +1749,7 @@ const useTokenSelectionMetadataStore = create((set) => ({
1708
1749
  volume,
1709
1750
  sumNetFunding,
1710
1751
  maxLeverage,
1711
- minMargin,
1752
+ minSize,
1712
1753
  leverageMatched,
1713
1754
  });
1714
1755
  },
@@ -1723,11 +1764,10 @@ const useTokenSelectionMetadata = () => {
1723
1764
  const tokenMetadata = useHyperliquidData((state) => state.tokenMetadata);
1724
1765
  const marketData = useMarketData((state) => state.marketData);
1725
1766
  const { longTokens, shortTokens } = useUserSelection$1();
1726
- const { isLoading, isPriceDataReady, longTokensMetadata, shortTokensMetadata, weightedRatio, weightedRatio24h, priceRatio, priceRatio24h, openInterest, volume, sumNetFunding, maxLeverage, minMargin, leverageMatched, recompute, } = useTokenSelectionMetadataStore();
1767
+ const { isLoading, isPriceDataReady, longTokensMetadata, shortTokensMetadata, weightedRatio, weightedRatio24h, priceRatio, priceRatio24h, openInterest, volume, sumNetFunding, maxLeverage, minSize, leverageMatched, recompute, } = useTokenSelectionMetadataStore();
1727
1768
  // Recompute derived metadata when inputs change
1728
1769
  useEffect(() => {
1729
1770
  recompute({
1730
- perpMetaAssets,
1731
1771
  tokenMetadata,
1732
1772
  marketData: marketData || null,
1733
1773
  longTokens,
@@ -1751,8 +1791,8 @@ const useTokenSelectionMetadata = () => {
1751
1791
  volume,
1752
1792
  sumNetFunding,
1753
1793
  maxLeverage,
1754
- minMargin,
1755
1794
  leverageMatched,
1795
+ minSize,
1756
1796
  };
1757
1797
  };
1758
1798
 
@@ -7197,122 +7237,62 @@ function useNotifications() {
7197
7237
  };
7198
7238
  }
7199
7239
 
7200
- // Convert a basket item to the expected type
7201
- function enrichBasketItem(item) {
7202
- const enrichedLongs = item.longAssets.map((a) => ({
7203
- ...a,
7204
- }));
7205
- const enrichedShorts = item.shortAssets.map((a) => ({
7206
- ...a,
7207
- }));
7240
+ function enrichBasketWithMetadata(basket, tokenMetadata) {
7208
7241
  return {
7209
- ...item,
7210
- longAssets: enrichedLongs,
7211
- shortAssets: enrichedShorts,
7212
- collateralType: 'USDC', //TODO: change
7242
+ ...basket,
7243
+ longAssets: basket.longAssets.map((asset) => {
7244
+ var _a;
7245
+ return ({
7246
+ ...asset,
7247
+ metadata: (_a = tokenMetadata[asset.asset]) !== null && _a !== void 0 ? _a : null,
7248
+ });
7249
+ }),
7250
+ shortAssets: basket.shortAssets.map((asset) => {
7251
+ var _a;
7252
+ return ({
7253
+ ...asset,
7254
+ metadata: (_a = tokenMetadata[asset.asset]) !== null && _a !== void 0 ? _a : null,
7255
+ });
7256
+ }),
7213
7257
  };
7214
7258
  }
7215
- /**
7216
- * Filter baskets by collateral type
7217
- * - 'USDC': Only baskets where ALL assets use USDC (collateralType === 'USDC')
7218
- * - 'USDH': Only baskets where ALL assets use USDH (collateralType === 'USDH')
7219
- * - 'ALL' or undefined: No filtering, returns all baskets
7220
- */
7221
- function filterByCollateral(baskets, filter) {
7222
- if (!filter || filter === 'ALL') {
7223
- return baskets;
7224
- }
7225
- return baskets.filter((basket) => {
7226
- if (filter === 'USDC') {
7227
- // Include baskets that are purely USDC or have USDC assets
7228
- return (basket.collateralType === 'USDC' || basket.collateralType === 'MIXED');
7229
- }
7230
- if (filter === 'USDH') {
7231
- // Include baskets that are purely USDH or have USDH assets
7232
- return (basket.collateralType === 'USDH' || basket.collateralType === 'MIXED');
7233
- }
7234
- return true;
7235
- });
7259
+ function enrichBasketsWithMetadata(baskets, tokenMetadata) {
7260
+ return baskets.map((basket) => enrichBasketWithMetadata(basket, tokenMetadata));
7236
7261
  }
7237
- // Base selector for the full market-data payload (raw from WS)
7238
- const useMarketDataPayload = () => {
7239
- return useMarketData((s) => s.marketData);
7240
- };
7241
- // Active baskets
7242
- const useActiveBaskets = (collateralFilter) => {
7243
- const data = useMarketDataPayload();
7244
- return useMemo(() => {
7245
- if (!(data === null || data === void 0 ? void 0 : data.active))
7246
- return [];
7247
- const enriched = data.active.map((item) => enrichBasketItem(item));
7248
- return filterByCollateral(enriched, collateralFilter);
7249
- }, [data, collateralFilter]);
7250
- };
7251
- // Top gainers
7252
- const useTopGainers = (limit, collateralFilter) => {
7253
- const data = useMarketDataPayload();
7254
- return useMemo(() => {
7255
- var _a;
7256
- const list = (_a = data === null || data === void 0 ? void 0 : data.topGainers) !== null && _a !== void 0 ? _a : [];
7257
- const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7258
- const enriched = limited.map((item) => enrichBasketItem(item));
7259
- return filterByCollateral(enriched, collateralFilter);
7260
- }, [data, limit, collateralFilter]);
7261
- };
7262
- // Top losers
7263
- const useTopLosers = (limit, collateralFilter) => {
7264
- const data = useMarketDataPayload();
7265
- return useMemo(() => {
7266
- var _a;
7267
- const list = (_a = data === null || data === void 0 ? void 0 : data.topLosers) !== null && _a !== void 0 ? _a : [];
7268
- const limited = typeof limit === 'number' ? list.slice(0, Math.max(0, limit)) : list;
7269
- const enriched = limited.map((item) => enrichBasketItem(item));
7270
- return filterByCollateral(enriched, collateralFilter);
7271
- }, [data, limit, collateralFilter]);
7272
- };
7273
- // Highlighted baskets
7274
- const useHighlightedBaskets = (collateralFilter) => {
7275
- const data = useMarketDataPayload();
7276
- return useMemo(() => {
7277
- if (!(data === null || data === void 0 ? void 0 : data.highlighted))
7278
- return [];
7279
- const enriched = data.highlighted.map((item) => enrichBasketItem(item));
7280
- return filterByCollateral(enriched, collateralFilter);
7281
- }, [data, collateralFilter]);
7282
- };
7283
- // Watchlist baskets
7284
- const useWatchlistBaskets = (collateralFilter) => {
7285
- const data = useMarketDataPayload();
7262
+ const useMarketDataHook = (options = {}) => {
7263
+ const { topGainersLimit, topLosersLimit } = options;
7264
+ const data = useMarketData((s) => s.marketData);
7265
+ const tokenMetadata = useHyperliquidData((s) => s.tokenMetadata);
7286
7266
  return useMemo(() => {
7287
- if (!(data === null || data === void 0 ? void 0 : data.watchlist))
7288
- return [];
7289
- const enriched = data.watchlist.map((item) => enrichBasketItem(item));
7290
- return filterByCollateral(enriched, collateralFilter);
7291
- }, [data, collateralFilter]);
7292
- };
7293
- // Find a basket by its exact asset composition (order-insensitive)
7294
- const useFindBasket = (longs, shorts) => {
7295
- const data = useMarketDataPayload();
7296
- return useMemo(() => {
7297
- if (!data)
7298
- return undefined;
7299
- const normalize = (arr) => Array.isArray(arr)
7300
- ? arr
7301
- .map((v) => (typeof v === 'string' ? v : v === null || v === void 0 ? void 0 : v.asset))
7302
- .filter(Boolean)
7303
- .map((s) => s.toUpperCase())
7304
- .sort()
7305
- .join(',')
7306
- : '';
7307
- const lKey = normalize(longs);
7308
- const sKey = normalize(shorts);
7309
- const match = (item) => normalize(item.longAssets) === lKey &&
7310
- normalize(item.shortAssets) === sKey;
7311
- const found = data.active.find(match) || data.highlighted.find(match);
7312
- return found
7313
- ? enrichBasketItem(found)
7314
- : undefined;
7315
- }, [data, longs, shorts]);
7267
+ var _a, _b, _c;
7268
+ const activeBaskets = enrichBasketsWithMetadata((_a = data === null || data === void 0 ? void 0 : data.active) !== null && _a !== void 0 ? _a : [], tokenMetadata);
7269
+ const topGainers = (() => {
7270
+ var _a;
7271
+ const list = (_a = data === null || data === void 0 ? void 0 : data.topGainers) !== null && _a !== void 0 ? _a : [];
7272
+ const limited = typeof topGainersLimit === 'number'
7273
+ ? list.slice(0, Math.max(0, topGainersLimit))
7274
+ : list;
7275
+ return enrichBasketsWithMetadata(limited, tokenMetadata);
7276
+ })();
7277
+ const topLosers = (() => {
7278
+ var _a;
7279
+ const list = (_a = data === null || data === void 0 ? void 0 : data.topLosers) !== null && _a !== void 0 ? _a : [];
7280
+ const limited = typeof topLosersLimit === 'number'
7281
+ ? list.slice(0, Math.max(0, topLosersLimit))
7282
+ : list;
7283
+ return enrichBasketsWithMetadata(limited, tokenMetadata);
7284
+ })();
7285
+ const highlightedBaskets = enrichBasketsWithMetadata((_b = data === null || data === void 0 ? void 0 : data.highlighted) !== null && _b !== void 0 ? _b : [], tokenMetadata);
7286
+ const watchlistBaskets = enrichBasketsWithMetadata((_c = data === null || data === void 0 ? void 0 : data.watchlist) !== null && _c !== void 0 ? _c : [], tokenMetadata);
7287
+ return {
7288
+ marketData: data,
7289
+ activeBaskets,
7290
+ topGainers,
7291
+ topLosers,
7292
+ highlightedBaskets,
7293
+ watchlistBaskets,
7294
+ };
7295
+ }, [data, tokenMetadata, topGainersLimit, topLosersLimit]);
7316
7296
  };
7317
7297
 
7318
7298
  async function toggleWatchlist(baseUrl, longAssets, shortAssets) {
@@ -7338,7 +7318,7 @@ function useWatchlist() {
7338
7318
  if (!context)
7339
7319
  throw new Error('useWatchlist must be used within a PearHyperliquidProvider');
7340
7320
  const { apiBaseUrl, isConnected } = context;
7341
- const marketData = useMarketDataPayload();
7321
+ const marketData = useMarketData(s => s.marketData);
7342
7322
  const isLoading = useMemo(() => !marketData && isConnected, [marketData, isConnected]);
7343
7323
  const toggle = async (longAssets, shortAssets) => {
7344
7324
  const resp = await toggleWatchlist(apiBaseUrl, longAssets, shortAssets);
@@ -7597,104 +7577,174 @@ function useAuth() {
7597
7577
 
7598
7578
  const useAllUserBalances = () => {
7599
7579
  const spotState = useUserData((state) => state.spotState);
7600
- const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
7601
- const rawClearinghouseStates = useHyperliquidData((state) => state.rawClearinghouseStates);
7602
7580
  const { longTokensMetadata, shortTokensMetadata } = useTokenSelectionMetadata();
7603
- return useMemo(() => {
7604
- const isLoading = !spotState || !aggregatedClearingHouseState;
7605
- // Helper function to truncate to 2 decimal places without rounding
7606
- const truncateToTwoDecimals = (value) => {
7607
- return Math.floor(value * 100) / 100;
7608
- };
7609
- // Get spot balances from spotState
7610
- let spotUsdcBal = undefined;
7611
- let spotUsdhBal = undefined;
7612
- if (spotState) {
7613
- const balances = spotState.balances || [];
7614
- for (const balance of balances) {
7581
+ const { longTokens, shortTokens } = useUserSelection$1();
7582
+ const aggregatedClearingHouseState = useHyperliquidData((state) => state.aggregatedClearingHouseState);
7583
+ const { spotBalances, availableToTrades, isLoading } = useMemo(() => {
7584
+ const isLoading = !spotState;
7585
+ const spotBalances = {};
7586
+ const availableToTrades = {};
7587
+ if (spotState === null || spotState === void 0 ? void 0 : spotState.balances) {
7588
+ for (const balance of spotState.balances) {
7615
7589
  const total = parseFloat(balance.total || '0');
7616
- if (balance.coin === 'USDC') {
7617
- spotUsdcBal = truncateToTwoDecimals(total);
7618
- }
7619
- if (balance.coin === 'USDH') {
7620
- spotUsdhBal = truncateToTwoDecimals(total);
7621
- }
7590
+ spotBalances[balance.coin] = total;
7622
7591
  }
7623
7592
  }
7624
- // Get available to trade from tokenMetadata for both USDC and USDH markets
7625
- let availableToTradeUsdcFromAsset = 0;
7626
- let availableToTradeUsdhFromAsset = 0;
7627
- // Token metadata only contains availableToTrade for SELECTED tokens (user's long and short Tokens)
7628
- // It does NOT contain data for all tokens, so we cannot reliably use it for available to trade as used on hl trade page
7629
- // so intead, we rely on rawClearinghouseStates which provides market-specific data
7630
- const selectedMetadataEntries = [
7631
- ...Object.entries(longTokensMetadata),
7632
- ...Object.entries(shortTokensMetadata),
7593
+ const allMetadataWithSource = [
7594
+ ...Object.values(longTokensMetadata).map((m) => ({
7595
+ metadata: m,
7596
+ isLong: true,
7597
+ })),
7598
+ ...Object.values(shortTokensMetadata).map((m) => ({
7599
+ metadata: m,
7600
+ isLong: false,
7601
+ })),
7633
7602
  ];
7634
- selectedMetadataEntries.forEach(([symbol, metadata]) => {
7635
- var _a;
7636
- const availableStr = (_a = metadata === null || metadata === void 0 ? void 0 : metadata.availableToTrade) === null || _a === void 0 ? void 0 : _a[0];
7637
- if (!availableStr)
7638
- return;
7639
- const availableValue = truncateToTwoDecimals(parseFloat(availableStr || '0'));
7640
- if ((metadata === null || metadata === void 0 ? void 0 : metadata.collateralToken) === 'USDH') {
7641
- availableToTradeUsdhFromAsset = Math.max(availableToTradeUsdhFromAsset, availableValue);
7642
- return;
7643
- }
7644
- if ((metadata === null || metadata === void 0 ? void 0 : metadata.collateralToken) === 'USDC') {
7645
- availableToTradeUsdcFromAsset = Math.max(availableToTradeUsdcFromAsset, availableValue);
7646
- return;
7647
- }
7648
- if (symbol.includes(':')) {
7649
- const prefix = symbol.split(':')[0];
7650
- if (prefix === 'xyz') {
7651
- availableToTradeUsdcFromAsset = Math.max(availableToTradeUsdcFromAsset, availableValue);
7652
- }
7653
- else {
7654
- availableToTradeUsdhFromAsset = Math.max(availableToTradeUsdhFromAsset, availableValue);
7655
- }
7656
- return;
7603
+ for (const { metadata, isLong } of allMetadataWithSource) {
7604
+ if (!(metadata === null || metadata === void 0 ? void 0 : metadata.collateralToken) || !(metadata === null || metadata === void 0 ? void 0 : metadata.availableToTrade))
7605
+ continue;
7606
+ const collateralCoin = metadata.collateralToken;
7607
+ const availableToTrade = metadata.availableToTrade;
7608
+ let value = parseFloat(availableToTrade[isLong ? 0 : 1] || '0');
7609
+ if (!(collateralCoin in availableToTrades) ||
7610
+ value < availableToTrades[collateralCoin]) {
7611
+ availableToTrades[collateralCoin] = value;
7657
7612
  }
7658
- availableToTradeUsdcFromAsset = Math.max(availableToTradeUsdcFromAsset, availableValue);
7659
- });
7660
- // Calculate USDC available to trade
7661
- // Priority 1: Use value from activeAssetData if available (> 0)
7662
- // Priority 2: Calculate from USDC-specific clearinghouseState (empty prefix)
7663
- let availableToTradeUsdcValue = undefined;
7664
- if (availableToTradeUsdcFromAsset > 0) {
7665
- availableToTradeUsdcValue = availableToTradeUsdcFromAsset;
7666
7613
  }
7667
- else if (rawClearinghouseStates) {
7668
- // Find USDC market (empty prefix)
7669
- const usdcMarket = rawClearinghouseStates.find(([prefix]) => prefix === '');
7670
- const usdcState = usdcMarket === null || usdcMarket === void 0 ? void 0 : usdcMarket[1];
7671
- if (usdcState === null || usdcState === void 0 ? void 0 : usdcState.marginSummary) {
7672
- const accountValue = parseFloat(usdcState.marginSummary.accountValue || '0');
7673
- const totalMarginUsed = parseFloat(usdcState.marginSummary.totalMarginUsed || '0');
7674
- const calculatedValue = Math.max(0, accountValue - totalMarginUsed);
7675
- availableToTradeUsdcValue = truncateToTwoDecimals(calculatedValue);
7676
- }
7614
+ if (!availableToTrades['USDC']) {
7615
+ availableToTrades['USDC'] = parseFloat((aggregatedClearingHouseState === null || aggregatedClearingHouseState === void 0 ? void 0 : aggregatedClearingHouseState.marginSummary.totalRawUsd) || '0');
7677
7616
  }
7678
- // Calculate USDH available to trade
7679
- // Priority 1: Use value from activeAssetData if available (> 0)
7680
- // Priority 2: Use spot USDH balance
7681
- const availableToTradeUsdhValue = availableToTradeUsdhFromAsset > 0
7682
- ? availableToTradeUsdhFromAsset
7683
- : spotUsdhBal;
7684
7617
  return {
7685
- spotUsdcBalance: spotUsdcBal,
7686
- availableToTradeUsdc: availableToTradeUsdcValue,
7687
- spotUsdhBalance: spotUsdhBal,
7688
- availableToTradeUsdh: availableToTradeUsdhValue,
7618
+ spotBalances,
7619
+ availableToTrades,
7689
7620
  isLoading,
7690
7621
  };
7622
+ }, [spotState, longTokensMetadata, shortTokensMetadata, aggregatedClearingHouseState]);
7623
+ /**
7624
+ * Calculate margin required for every collateral token based on asset leverages and size.
7625
+ * Returns margin required per collateral and whether there's sufficient margin.
7626
+ */
7627
+ const getMarginRequired = useCallback((assetsLeverage, size) => {
7628
+ const sizeValue = parseFloat(String(size)) || 0;
7629
+ // Group tokens by collateral type and calculate margin required
7630
+ const marginByCollateral = {};
7631
+ // Process all tokens (long and short)
7632
+ const allTokensWithMetadata = [
7633
+ ...longTokens.map((t) => ({
7634
+ ...t,
7635
+ metadata: longTokensMetadata[t.symbol],
7636
+ })),
7637
+ ...shortTokens.map((t) => ({
7638
+ ...t,
7639
+ metadata: shortTokensMetadata[t.symbol],
7640
+ })),
7641
+ ];
7642
+ let totalMarginRequired = 0;
7643
+ allTokensWithMetadata.forEach((token) => {
7644
+ var _a;
7645
+ const weight = token.weight || 0;
7646
+ const assetSize = (sizeValue * weight) / 100;
7647
+ const assetLeverage = assetsLeverage[token.symbol] || 1;
7648
+ const assetMargin = assetLeverage > 0 ? assetSize / assetLeverage : 0;
7649
+ // Get collateral type from metadata, default to USDC
7650
+ const collateralToken = ((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.collateralToken) || 'USDC';
7651
+ if (!marginByCollateral[collateralToken]) {
7652
+ marginByCollateral[collateralToken] = 0;
7653
+ }
7654
+ marginByCollateral[collateralToken] += assetMargin;
7655
+ totalMarginRequired += assetMargin;
7656
+ });
7657
+ const perCollateral = Object.entries(marginByCollateral).map(([collateral, marginRequired]) => {
7658
+ var _a;
7659
+ const collateralToken = collateral;
7660
+ const availableBalance = (_a = availableToTrades[collateralToken]) !== null && _a !== void 0 ? _a : 0;
7661
+ const hasEnough = marginRequired <= availableBalance;
7662
+ const shortfall = Math.max(0, marginRequired - availableBalance);
7663
+ return {
7664
+ collateral: collateralToken,
7665
+ marginRequired: Number(marginRequired.toFixed(2)),
7666
+ availableBalance: Number(availableBalance.toFixed(2)),
7667
+ hasEnough,
7668
+ shortfall: Number(shortfall.toFixed(2)),
7669
+ };
7670
+ });
7671
+ const hasEnoughTotal = perCollateral.every((c) => c.hasEnough);
7672
+ return {
7673
+ totalMarginRequired: Number(totalMarginRequired.toFixed(2)),
7674
+ orderValue: sizeValue,
7675
+ perCollateral,
7676
+ hasEnoughTotal,
7677
+ };
7678
+ }, [
7679
+ longTokens,
7680
+ shortTokens,
7681
+ longTokensMetadata,
7682
+ shortTokensMetadata,
7683
+ spotBalances,
7684
+ availableToTrades,
7685
+ ]);
7686
+ /**
7687
+ * Calculate the maximum order size ($) based on available balances and asset leverages.
7688
+ * Returns the overall maximum order size constrained by all collateral types.
7689
+ */
7690
+ const getMaxSize = useCallback((assetsLeverage) => {
7691
+ const marginFactorByCollateral = {};
7692
+ const allTokensWithMetadata = [
7693
+ ...longTokens.map((t) => ({
7694
+ ...t,
7695
+ metadata: longTokensMetadata[t.symbol],
7696
+ })),
7697
+ ...shortTokens.map((t) => ({
7698
+ ...t,
7699
+ metadata: shortTokensMetadata[t.symbol],
7700
+ })),
7701
+ ];
7702
+ // Calculate the margin factor for each collateral type
7703
+ // marginFactor = sum of (weight / leverage) for all assets using that collateral
7704
+ allTokensWithMetadata.forEach((token) => {
7705
+ var _a;
7706
+ const weight = token.weight || 0;
7707
+ const assetLeverage = assetsLeverage[token.symbol] || 1;
7708
+ const collateralToken = ((_a = token.metadata) === null || _a === void 0 ? void 0 : _a.collateralToken) || 'USDC';
7709
+ // marginFactor represents how much margin is needed per $1 of order size
7710
+ const marginFactor = assetLeverage > 0 ? weight / (100 * assetLeverage) : 0;
7711
+ if (!marginFactorByCollateral[collateralToken]) {
7712
+ marginFactorByCollateral[collateralToken] = 0;
7713
+ }
7714
+ marginFactorByCollateral[collateralToken] += marginFactor;
7715
+ });
7716
+ // Calculate max size for each collateral type
7717
+ // maxSize = availableBalance / marginFactor
7718
+ // Each maxSize represents the total order size that collateral can support
7719
+ const maxSizePerCollateral = [];
7720
+ Object.entries(marginFactorByCollateral).forEach(([collateral, marginFactor]) => {
7721
+ var _a;
7722
+ const collateralToken = collateral;
7723
+ const availableBalance = (_a = availableToTrades[collateralToken]) !== null && _a !== void 0 ? _a : 0;
7724
+ if (marginFactor > 0) {
7725
+ const maxSize = availableBalance / marginFactor;
7726
+ maxSizePerCollateral.push(maxSize);
7727
+ }
7728
+ });
7729
+ if (maxSizePerCollateral.length === 0) {
7730
+ return 0;
7731
+ }
7732
+ const overallMaxSize = Math.min(...maxSizePerCollateral);
7733
+ return Number(overallMaxSize.toFixed(2));
7691
7734
  }, [
7692
- spotState,
7693
- aggregatedClearingHouseState,
7694
- rawClearinghouseStates,
7735
+ longTokens,
7736
+ shortTokens,
7695
7737
  longTokensMetadata,
7696
7738
  shortTokensMetadata,
7739
+ availableToTrades,
7697
7740
  ]);
7741
+ return {
7742
+ spotBalances,
7743
+ availableToTrades,
7744
+ isLoading,
7745
+ getMarginRequired,
7746
+ getMaxSize,
7747
+ };
7698
7748
  };
7699
7749
 
7700
7750
  /**
@@ -7879,7 +7929,7 @@ const PearHyperliquidProvider = ({ children, apiBaseUrl = 'https://hl-ui.pearpro
7879
7929
  collateralToken = 'USDE';
7880
7930
  }
7881
7931
  if (item.collateralToken === 268) {
7882
- collateralToken = 'USDT';
7932
+ collateralToken = 'USDT0';
7883
7933
  }
7884
7934
  const universeAssets = item.universe.map((asset) => ({
7885
7935
  ...asset,
@@ -8175,4 +8225,4 @@ function getOrderTrailingInfo(order) {
8175
8225
  return undefined;
8176
8226
  }
8177
8227
 
8178
- export { AccountSummaryCalculator, ConflictDetector, MAX_ASSETS_PER_LEG, MINIMUM_ASSET_USD_VALUE, MaxAssetsPerLegError, MinimumPositionSizeError, PearHyperliquidProvider, TokenMetadataExtractor, adjustAdvancePosition, adjustOrder, adjustPosition, calculateMinimumPositionValue, calculateWeightedRatio, cancelOrder, cancelTwap, cancelTwapOrder, closeAllPositions, closePosition, computeBasketCandles, createCandleLookups, createPosition, executeSpotOrder, getAssetByName, getCompleteTimestamps, getKalshiMarkets, getOrderDirection, getOrderLadderConfig, getOrderLeverage, getOrderReduceOnly, getOrderTpSlTriggerType, getOrderTrailingInfo, getOrderTriggerType, getOrderTriggerValue, getOrderTwapDuration, getOrderUsdValue, getPortfolio, isBtcDomOrder, mapCandleIntervalToTradingViewInterval, mapTradingViewIntervalToCandleInterval, markNotificationReadById, markNotificationsRead, selectTokenMetadataBySymbols, toggleWatchlist, updateLeverage, updateRiskParameters, useAccountSummary, useActiveBaskets, useAgentWallet, useAllUserBalances, useAuth, useBasketCandles, useFindBasket, useHighlightedBaskets, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidNativeWebSocket, useHyperliquidUserFills, useHyperliquidWebSocket, useMarket, useMarketData, useMarketDataPayload, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePortfolio, usePosition, useSpotOrder, useTokenSelectionMetadata, useTopGainers, useTopLosers, useTradeHistories, useTwap, useUserSelection, useWatchlist, useWatchlistBaskets, validateMaxAssetsPerLeg, validateMinimumAssetSize, validatePositionSize };
8228
+ export { ConflictDetector, MAX_ASSETS_PER_LEG, MINIMUM_ASSET_USD_VALUE, MaxAssetsPerLegError, MinimumPositionSizeError, PearHyperliquidProvider, adjustAdvancePosition, adjustOrder, adjustPosition, calculateMinimumPositionValue, calculateWeightedRatio, cancelOrder, cancelTwap, cancelTwapOrder, closeAllPositions, closePosition, computeBasketCandles, createCandleLookups, createPosition, executeSpotOrder, getCompleteTimestamps, getKalshiMarkets, getOrderDirection, getOrderLadderConfig, getOrderLeverage, getOrderReduceOnly, getOrderTpSlTriggerType, getOrderTrailingInfo, getOrderTriggerType, getOrderTriggerValue, getOrderTwapDuration, getOrderUsdValue, getPortfolio, isBtcDomOrder, mapCandleIntervalToTradingViewInterval, mapTradingViewIntervalToCandleInterval, markNotificationReadById, markNotificationsRead, toggleWatchlist, updateLeverage, updateRiskParameters, useAccountSummary, useAgentWallet, useAllUserBalances, useAuth, useBasketCandles, useHistoricalPriceData, useHistoricalPriceDataStore, useHyperliquidUserFills, useMarket, useMarketData, useMarketDataHook, useNotifications, useOpenOrders, useOrders, usePearHyperliquid, usePerformanceOverlays, usePortfolio, usePosition, useSpotOrder, useTokenSelectionMetadata, useTradeHistories, useTwap, useUserSelection, useWatchlist, validateMaxAssetsPerLeg, validateMinimumAssetSize, validatePositionSize };
@@ -1,4 +1,4 @@
1
- import type { TokenSelection, TokenMetadata, ActiveAssetsResponse, UniverseAsset } from "../types";
1
+ import type { TokenSelection, TokenMetadata, ActiveAssetsResponse } from "../types";
2
2
  export interface TokenSelectionMetadataState {
3
3
  isPriceDataReady: boolean;
4
4
  isLoading: boolean;
@@ -12,10 +12,9 @@ export interface TokenSelectionMetadataState {
12
12
  volume: string;
13
13
  sumNetFunding: number;
14
14
  maxLeverage: number;
15
- minMargin: number;
15
+ minSize: Record<string, number>;
16
16
  leverageMatched: boolean;
17
17
  recompute: (args: {
18
- perpMetaAssets: UniverseAsset[] | null;
19
18
  tokenMetadata: Record<string, TokenMetadata | null>;
20
19
  marketData: ActiveAssetsResponse | null;
21
20
  longTokens: TokenSelection[];