npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hfunlabs/hypurr-connect

v0.1.3

Published

React authentication and wallet connectivity library for the [Hyperliquid](https://hyperliquid.xyz) decentralized exchange via the [Hypurr](https://hypurr.fun) gRPC backend. Provides two authentication paths — **Telegram OAuth** and **EOA wallet** (MetaMa

Readme

@hfunlabs/hypurr-connect

React authentication and wallet connectivity library for the Hyperliquid decentralized exchange via the Hypurr gRPC backend. Provides two authentication paths — Telegram OAuth and EOA wallet (MetaMask / browser wallet) — with a unified ExchangeClient API for placing orders, managing positions, and executing both L1 and user-signed actions.

Features

  • Dual auth flows — Telegram OAuth (server-side signing) and EOA wallet (client-side signing)
  • Unified exchange client — Same ExchangeClient interface regardless of auth method; handles both L1 actions (orders, cancels, leverage) and user-signed actions (transfers, withdrawals, staking)
  • Dual wallet routing — EOA exchange client automatically routes L1 actions through the agent key (silent) and user-signed actions through the master wallet (wallet popup)
  • Auto agent provisioning — Agent keys are created on-the-fly the first time an L1 action is executed; no manual approveAgent step required when a signer is provided
  • Multi-wallet management — Switch between wallets, create/delete wallets, manage wallet packs and labels (Telegram users)
  • gRPC transport — Custom transport that routes exchange actions through the Hypurr backend for Telegram users
  • Agent key management — Named agent keys ("hypurr-connect") with on-chain approval, extraAgents validation, expiry-aware caching, and automatic dead-agent recovery
  • Session persistence — Auth state survives page reloads via localStorage
  • Responsive login modal — Centered modal on desktop, bottom drawer on mobile with framer-motion animations

Installation

pnpm add @hfunlabs/hypurr-connect

Peer Dependencies

Install the required peer dependencies:

pnpm add @hfunlabs/hyperliquid @protobuf-ts/grpcweb-transport @protobuf-ts/runtime-rpc framer-motion react

| Peer Dependency | Version | | -------------------------------- | ------------------- | | @hfunlabs/hyperliquid | 0.30.2-hfunlabs.2 | | @protobuf-ts/grpcweb-transport | >=2.0.0 | | @protobuf-ts/runtime-rpc | >=2.0.0 | | framer-motion | >=10.0.0 | | react | >=18.0.0 |

Quick Start

1. Wrap your app with the provider

import { HypurrConnectProvider } from "@hfunlabs/hypurr-connect";

const config = {
  isTestnet: false,
  telegram: {
    botUsername: "YourBot",
    botId: "123456789",
    useWidget: true,
  },
};

function App() {
  return (
    <HypurrConnectProvider config={config}>
      <YourApp />
    </HypurrConnectProvider>
  );
}

2. Use the hook anywhere in your app

import { useHypurrConnect } from "@hfunlabs/hypurr-connect";

function TradingPanel() {
  const { user, isLoggedIn, exchange, openLoginModal, logout } =
    useHypurrConnect();

  if (!isLoggedIn) {
    return <button onClick={openLoginModal}>Connect</button>;
  }

  return (
    <div>
      <p>Welcome, {user.displayName}</p>
      <button onClick={logout}>Disconnect</button>
    </div>
  );
}

3. Add the login modal

import { LoginModal, useHypurrConnect } from "@hfunlabs/hypurr-connect";

function AppShell() {
  const { loginModalOpen, closeLoginModal } = useHypurrConnect();

  return (
    <>
      {loginModalOpen && (
        <LoginModal
          onConnectWallet={() => {
            // Open your wagmi/RainbowKit wallet modal here
          }}
        />
      )}
      <MainContent />
    </>
  );
}

Configuration

HypurrConnectConfig

interface HypurrConnectConfig {
  grpcTimeout?: number; // Request timeout in ms (default: 15000)
  isTestnet?: boolean; // Use testnet endpoints (default: false)
  telegram: {
    botUsername: string; // Telegram bot username (required for the widget)
    botId?: string; // Telegram bot ID (required for the popup OAuth flow)
    useWidget: boolean; // true = inline Telegram Login Widget, false = popup OAuth
  };
}

When useWidget is true, the login modal renders Telegram's official Login Widget inline — no popup window is opened. This avoids popup-blocker issues and shows users the familiar Telegram button directly inside the modal. The widget requires botUsername and that your domain is linked to the bot via /setdomain in @BotFather.

When useWidget is false (or omitted), the popup OAuth flow is used, which requires botId.

Dependencies

This package depends on hypurr-grpc (public GitLab repo) for generated protobuf service clients. It is installed directly from Git — no registry auth is needed.

Authentication Flows

Telegram Login

Two modes are available, controlled by config.telegram.useWidget:

Widget mode (useWidget: true) — recommended:

  1. The LoginModal renders Telegram's official Login Widget inline
  2. User clicks the widget button and authorizes with Telegram
  3. The widget calls the onAuth callback with the user's auth data
  4. The provider calls the Hypurr gRPC backend (telegramUser) to fetch the user's wallet address and ID
  5. An ExchangeClient is created with GrpcExchangeTransport — all exchange actions are signed server-side by the Hypurr backend
  6. Session is persisted in localStorage (hypurr-connect-tg-user)

Popup mode (useWidget: false):

  1. User clicks "Telegram" in the LoginModal
  2. A popup opens to oauth.telegram.org with the configured bot
  3. User authorizes; Telegram posts auth data back via postMessage
  4. Steps 4–6 are identical to widget mode

EOA Wallet Login

When a signer is provided via connectEoa, the exchange client works immediately — no manual agent approval step is needed:

  1. User clicks "Wallet" in the LoginModal; the onConnectWallet callback fires
  2. Your app connects the wallet (e.g., via wagmi) and calls connectEoa(address, signer) with an EoaSigner — this sets the user immediately and restores a cached agent from localStorage if one is still valid
  3. The exchange client is ready to use right away:
    • User-signed actions (transfers, withdrawals, staking) are routed directly to the master wallet — the wallet popup appears for the user to approve
    • L1 actions (orders, cancels, leverage) use the agent key. If no agent exists yet, one is auto-provisioned on the first L1 call — this triggers a single wallet popup for the approveAgent signature, then the original action proceeds
    • Named agent keys ("hypurr-connect") are cached in localStorage with their validUntil timestamp and revalidated against Hyperliquid's extraAgents endpoint
  4. If an exchange action fails because the agent was pruned or expired, the SDK automatically clears the dead agent and surfaces the error via error

Use the createEoaSigner helper to adapt wagmi's signTypedDataAsync to the EoaSigner interface. Pass a ref to avoid stale closure issues with React hooks:

import { useHypurrConnect, createEoaSigner } from "@hfunlabs/hypurr-connect";
import { useSignTypedData, useChainId, useAccountEffect } from "wagmi";

function ConnectWallet() {
  const { connectEoa, logout, exchange } = useHypurrConnect();
  const { signTypedDataAsync } = useSignTypedData();
  const chainId = useChainId();

  // Keep a ref so the signer always calls the latest signTypedDataAsync
  const signerRef = useRef(signTypedDataAsync);
  signerRef.current = signTypedDataAsync;

  useAccountEffect({
    onConnect({ address }) {
      connectEoa(address, createEoaSigner(signerRef, chainId));
    },
    onDisconnect() {
      logout();
    },
  });

  // exchange is ready — L1 and user-signed actions both work
  if (exchange) {
    // L1 action — agent signs silently (auto-provisioned if needed)
    await exchange.order({ orders: [/* ... */], grouping: "na" });

    // User-signed action — wallet popup appears
    await exchange.usdSend({ destination: "0x...", amount: "100" });
  }
}

If you prefer explicit control over agent approval (or don't need user-signed actions), you can omit the signer and call approveAgent manually:

connectEoa(address); // no signer — only L1 actions after manual approval
await approveAgent(signTypedDataAsync, chainId);

Using the Exchange Client

Once authenticated, the exchange object from useHypurrConnect() is a fully functional ExchangeClient from @hfunlabs/hyperliquid. For EOA users with a signer, it handles both L1 and user-signed actions transparently:

const { exchange } = useHypurrConnect();

if (exchange) {
  // L1 actions — signed by the agent key (no wallet popup)
  await exchange.order({
    orders: [
      {
        a: 0,
        b: true,
        p: "50000",
        s: "0.001",
        t: { limit: { tif: "Gtc" } },
      },
    ],
    grouping: "na",
  });

  await exchange.cancelOrder({ asset: 0, oid: 12345 });

  // User-signed actions — signed by the master wallet (wallet popup)
  await exchange.usdSend({ destination: "0x...", amount: "100" });
  await exchange.withdraw({ destination: "0x...", amount: "50" });
  await exchange.spotSend({
    destination: "0x...",
    token: "PURR:0x...",
    amount: "10",
  });
}

The routing is automatic — no need to call different methods or switch signers.

Multi-Wallet Management (Telegram)

Telegram users can have multiple wallets. The library exposes the full wallet list and lets you switch the active wallet — the exchange client and user automatically update.

const {
  wallets,
  selectedWalletId,
  selectWallet,
  createWallet,
  deleteWallet,
  refreshWallets,
} = useHypurrConnect();

// List wallets
wallets.map((w) => (
  <button
    key={w.id}
    onClick={() => selectWallet(w.id)}
    style={{ fontWeight: w.id === selectedWalletId ? "bold" : "normal" }}
  >
    {w.name || w.ethereumAddress}
  </button>
));

// Create a new wallet
const newWallet = await createWallet("Trading");

// Delete a wallet (auto-selects another if the deleted one was active)
await deleteWallet(walletId);

Wallet Packs & Labels

Organize watched wallets into named packs with labels:

const {
  packs,
  createWalletPack,
  addPackLabel,
  modifyPackLabel,
  removePackLabel,
} = useHypurrConnect();

// Create a pack
const packId = await createWalletPack("Whales");

// Add a labeled wallet to the pack
await addPackLabel({
  walletAddress: "0x...",
  walletLabel: "Big trader",
  packId,
});

// Rename a label
await modifyPackLabel({
  walletLabelOld: "Big trader",
  walletLabelNew: "Whale #1",
  packId,
});

// Remove a label from the pack
await removePackLabel({ walletLabel: "Whale #1", packId });

All wallet management functions automatically refresh the wallet list from the server after mutations.

API Reference

Components

HypurrConnectProvider

<HypurrConnectProvider config={HypurrConnectConfig}>
  {children}
</HypurrConnectProvider>

Context provider that manages all auth state, gRPC clients, and exchange clients. Must wrap any component that uses useHypurrConnect.

LoginModal

<LoginModal
  onConnectWallet={() => void}
  walletIcon?: ReactNode       // Custom icon for the wallet button (defaults to MetaMask icon)
/>

Animated modal with Telegram and Wallet login buttons. Renders as a centered modal on desktop (>=640px) and a bottom drawer on mobile. Uses the closeLoginModal function from context to dismiss.

Hook

useHypurrConnect(): HypurrConnectState

Returns the full auth and exchange state. Throws if used outside HypurrConnectProvider.

HypurrConnectState

| Property | Type | Description | | ------------------ | ------------------------------------------------------------------------- | ---------------------------------------------------------------- | | user | HypurrUser \| null | Current authenticated user (reflects selected wallet) | | isLoggedIn | boolean | Whether a user is authenticated | | isLoading | boolean | Whether auth is in progress | | error | string \| null | Last auth or dead-agent error message | | authMethod | AuthMethod | "telegram", "eoa", or null | | exchange | ExchangeClient \| null | Hyperliquid exchange client (L1 + user-signed actions for EOA) | | wallets | HyperliquidWallet[] | All wallets for the Telegram user (empty for EOA) | | selectedWalletId | number | ID of the currently active wallet | | selectWallet | (walletId: number) => void | Switch the active wallet | | createWallet | (name: string) => Promise<HyperliquidWallet> | Create a new wallet (Telegram only) | | deleteWallet | (walletId: number) => Promise<void> | Delete a wallet (Telegram only) | | refreshWallets | () => void | Re-fetch wallets and packs from the server | | packs | TelegramChatWalletPack[] | Wallet packs for the Telegram user | | createWalletPack | (name: string) => Promise<number> | Create a wallet pack; returns the new pack ID | | addPackLabel | (params) => Promise<void> | Add a labeled wallet to a pack | | modifyPackLabel | (params) => Promise<void> | Rename a label within a pack | | removePackLabel | (params) => Promise<void> | Remove a label from a pack | | loginModalOpen | boolean | Whether the login modal is visible | | openLoginModal | () => void | Show the login modal | | closeLoginModal | () => void | Hide the login modal | | connectEoa | (address: \0x${string}`, signer?: EoaSigner) => void | Connect EOA wallet (sync); pass signer to enable user-signed actions and auto-provisioning | |approveAgent |(signTypedDataAsync: SignTypedDataFn, chainId: number) => Promise| Approve a named agent key (async, triggers wallet prompt) | |logout |() => void | Clear all auth state and localStorage | |agent |StoredAgent | null | Current agent key (EOA flow only) | |agentReady |boolean | Whether the exchange client can sign (true for TG, or EOA+agent) | |clearAgent |() => void | Remove the agent key from state and storage | |botId |string | Telegram bot ID from config | |authDataMap |Record<string, string> | Raw Telegram auth data as key-value pairs | |telegramClient |TelegramClient | Low-level gRPC client for the Telegram service | |staticClient |StaticClient` | Low-level gRPC client for the Static service |

Types

HypurrUser

interface HypurrUser {
  address: string; // Ethereum address
  walletId: number; // Hypurr wallet ID
  displayName: string; // "@username", first_name, or truncated address
  photoUrl?: string; // Telegram profile photo URL
  authMethod: AuthMethod; // "telegram" | "eoa"
  telegramId?: string; // Telegram user ID (string)
}

AuthMethod

type AuthMethod = "telegram" | "eoa" | null;

TelegramLoginData

interface TelegramLoginData {
  id: number;
  first_name: string;
  last_name?: string;
  username?: string;
  photo_url?: string;
  auth_date: number;
  hash: string;
}

StoredAgent

interface StoredAgent {
  privateKey: `0x${string}`;
  address: `0x${string}`;
  approvedAt: number; // Timestamp when approved on-chain
  validUntil: number; // Epoch ms from extraAgents; agent is invalid after this
}

HyperliquidWallet

Re-exported from hypurr-grpc:

interface HyperliquidWallet {
  id: number;
  name: string;
  ethereumAddress: string;
  isAgent: boolean;
  isReadOnly: boolean;
  // ... plus balances, movements, sessions
}

TelegramChatWalletPack

Re-exported from hypurr-grpc:

interface TelegramChatWalletPack {
  id: number;
  telegramChatId: number;
  name: string;
}

EoaSigner

Encapsulates the master wallet's EIP-712 signing function and chain ID. Pass to connectEoa to enable user-signed actions and auto agent provisioning.

interface EoaSigner {
  signTypedData: SignTypedDataFn;
  chainId: number;
}

createEoaSigner(signTypedData, chainId): EoaSigner

Helper to create an EoaSigner from wagmi's signTypedDataAsync (or any compatible function). Accepts either a direct function or a { current: Function } ref to avoid stale closures with React hooks:

// With a ref (recommended for React — always calls the latest function)
const signerRef = useRef(signTypedDataAsync);
signerRef.current = signTypedDataAsync;
const signer = createEoaSigner(signerRef, chainId);

// With a direct function (fine for stable references)
const signer = createEoaSigner(signTypedDataAsync, chainId);

SignTypedDataFn

type SignTypedDataFn = (params: {
  domain: Record<string, unknown>;
  types: Record<string, { name: string; type: string }[]>;
  primaryType: string;
  message: Record<string, unknown>;
}) => Promise<`0x${string}`>;

Classes

GrpcExchangeTransport

Custom IRequestTransport implementation that routes exchange actions through the Hypurr gRPC backend (used by Telegram auth).

class GrpcExchangeTransport implements IRequestTransport {
  isTestnet: boolean;
  constructor(config: GrpcExchangeTransportConfig);
  request<T>(
    endpoint: "info" | "exchange" | "explorer",
    payload: unknown,
    signal?: AbortSignal,
  ): Promise<T>;
}
  • exchange endpoint — Serializes the action to bytes and calls telegramClient.hyperliquidCoreAction() via gRPC. The Hypurr backend validates the auth data and signs the action server-side.
  • info / explorer endpoints — Proxied directly to the Hyperliquid HTTP API.
interface GrpcExchangeTransportConfig {
  isTestnet?: boolean;
  telegramClient: TelegramClient;
  authDataMap: Record<string, string>;
  walletId: number;
}

Factory Functions

createTelegramClient(config: HypurrConnectConfig): TelegramClient

Creates a gRPC-Web client for the Telegram service.

createStaticClient(config: HypurrConnectConfig): StaticClient

Creates a gRPC-Web client for the Static service.

Both use GrpcWebFetchTransport with the configured baseUrl, timeout, and origin metadata.

localStorage Keys

| Key | Content | | -------------------------------- | -------------------------------------------------------------- | | hypurr-connect-tg-user | Serialized TelegramLoginData (persists Telegram session) | | hypurr-connect-agent:{address} | Serialized StoredAgent (persists EOA agent keys per address) |

Architecture

┌──────────────────────────────────────────────────────────────────┐
│                      HypurrConnectProvider                        │
│                                                                  │
│  ┌──────────────────┐       ┌──────────────────────────────┐     │
│  │  Telegram Auth    │─gRPC─▶│  GrpcExchangeTransport        │     │
│  │  (server-signed)  │       │  telegramClient                │     │
│  └──────────────────┘       │    .hyperliquidCoreAction()    │     │
│                              └──────────────────────────────┘     │
│                                                                  │
│  ┌──────────────────┐       ┌──────────────────────────────┐     │
│  │    EOA Auth       │─HTTP─▶│  HttpTransport + Dual Wallet   │     │
│  │  (client-signed)  │       │                                │     │
│  └──────────────────┘       │  signTypedData(params) ─┐      │     │
│                              │                         │      │     │
│                              │  domain = "Exchange"    │      │     │
│                              │    └─▶ Agent Key        │      │     │
│                              │        (auto-provision  │      │     │
│                              │         if needed)      │      │     │
│                              │                         │      │     │
│                              │  domain = "Hyperliquid  │      │     │
│                              │   SignTransaction"      │      │     │
│                              │    └─▶ Master Wallet    │      │     │
│                              │        (wallet popup)   │      │     │
│                              └──────────────────────────────┘     │
│                                          │                        │
│                                          ▼                        │
│                                 ┌──────────────┐                 │
│                                 │ExchangeClient│                 │
│                                 └──────────────┘                 │
│                                          │                        │
│                                          ▼                        │
│                                  Hyperliquid L1                   │
└──────────────────────────────────────────────────────────────────┘

License

MIT