@hfunlabs/hypurr-connect 0.1.1 → 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/README.md CHANGED
@@ -1,16 +1,18 @@
1
1
  # @hfunlabs/hypurr-connect
2
2
 
3
- 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** (MetaMask / browser wallet) — with a unified `ExchangeClient` API for placing orders, managing positions, and executing any L1 action.
3
+ 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** (MetaMask / browser wallet) — with a unified `ExchangeClient` API for placing orders, managing positions, and executing both L1 and user-signed actions.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Dual auth flows** — Telegram OAuth (server-side signing) and EOA wallet (client-side agent key signing)
8
- - **Unified exchange client** — Same `ExchangeClient` interface regardless of auth method
7
+ - **Dual auth flows** — Telegram OAuth (server-side signing) and EOA wallet (client-side signing)
8
+ - **Unified exchange client** — Same `ExchangeClient` interface regardless of auth method; handles both L1 actions (orders, cancels, leverage) and user-signed actions (transfers, withdrawals, staking)
9
+ - **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)
10
+ - **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
11
+ - **Multi-wallet management** — Switch between wallets, create/delete wallets, manage wallet packs and labels (Telegram users)
9
12
  - **gRPC transport** — Custom transport that routes exchange actions through the Hypurr backend for Telegram users
10
- - **Agent key management** — Automatic generation, on-chain approval, and localStorage persistence of agent keys for EOA wallets
13
+ - **Agent key management** — Named agent keys (`"hypurr-connect"`) with on-chain approval, `extraAgents` validation, expiry-aware caching, and automatic dead-agent recovery
11
14
  - **Session persistence** — Auth state survives page reloads via localStorage
12
15
  - **Responsive login modal** — Centered modal on desktop, bottom drawer on mobile with framer-motion animations
13
- - **USDC balance tracking** — Built-in clearinghouse balance fetching with manual refresh
14
16
 
15
17
  ## Installation
16
18
 
@@ -46,6 +48,7 @@ const config = {
46
48
  telegram: {
47
49
  botUsername: "YourBot",
48
50
  botId: "123456789",
51
+ useWidget: true,
49
52
  },
50
53
  };
51
54
 
@@ -64,7 +67,7 @@ function App() {
64
67
  import { useHypurrConnect } from "@hfunlabs/hypurr-connect";
65
68
 
66
69
  function TradingPanel() {
67
- const { user, isLoggedIn, exchange, usdcBalance, openLoginModal, logout } =
70
+ const { user, isLoggedIn, exchange, openLoginModal, logout } =
68
71
  useHypurrConnect();
69
72
 
70
73
  if (!isLoggedIn) {
@@ -74,7 +77,6 @@ function TradingPanel() {
74
77
  return (
75
78
  <div>
76
79
  <p>Welcome, {user.displayName}</p>
77
- <p>Balance: {usdcBalance} USDC</p>
78
80
  <button onClick={logout}>Disconnect</button>
79
81
  </div>
80
82
  );
@@ -112,14 +114,17 @@ function AppShell() {
112
114
  interface HypurrConnectConfig {
113
115
  grpcTimeout?: number; // Request timeout in ms (default: 15000)
114
116
  isTestnet?: boolean; // Use testnet endpoints (default: false)
115
- telegram?: {
116
- botUsername: string; // Telegram bot username for the login widget
117
- botId: string; // Telegram bot ID for the OAuth URL
117
+ telegram: {
118
+ botUsername: string; // Telegram bot username (required for the widget)
119
+ botId?: string; // Telegram bot ID (required for the popup OAuth flow)
120
+ useWidget: boolean; // true = inline Telegram Login Widget, false = popup OAuth
118
121
  };
119
122
  }
120
123
  ```
121
124
 
122
- The `telegram` block is only required if you want to support Telegram login.
125
+ When `useWidget` is `true`, the login modal renders Telegram's official [Login Widget](https://core.telegram.org/widgets/login) 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](https://t.me/botfather).
126
+
127
+ When `useWidget` is `false` (or omitted), the popup OAuth flow is used, which requires `botId`.
123
128
 
124
129
  ### Dependencies
125
130
 
@@ -129,44 +134,183 @@ This package depends on [`hypurr-grpc`](https://gitlab.com/hypurr/hypurr-grpc) (
129
134
 
130
135
  ### Telegram Login
131
136
 
132
- 1. User clicks "Telegram" in the `LoginModal`
133
- 2. A popup opens to `oauth.telegram.org` with the configured bot
134
- 3. User authorizes; Telegram posts auth data back via `postMessage`
137
+ Two modes are available, controlled by `config.telegram.useWidget`:
138
+
139
+ **Widget mode** (`useWidget: true`) recommended:
140
+
141
+ 1. The `LoginModal` renders Telegram's official [Login Widget](https://core.telegram.org/widgets/login) inline
142
+ 2. User clicks the widget button and authorizes with Telegram
143
+ 3. The widget calls the `onAuth` callback with the user's auth data
135
144
  4. The provider calls the Hypurr gRPC backend (`telegramUser`) to fetch the user's wallet address and ID
136
145
  5. An `ExchangeClient` is created with `GrpcExchangeTransport` — all exchange actions are signed **server-side** by the Hypurr backend
137
146
  6. Session is persisted in localStorage (`hypurr-connect-tg-user`)
138
147
 
148
+ **Popup mode** (`useWidget: false`):
149
+
150
+ 1. User clicks "Telegram" in the `LoginModal`
151
+ 2. A popup opens to `oauth.telegram.org` with the configured bot
152
+ 3. User authorizes; Telegram posts auth data back via `postMessage`
153
+ 4. Steps 4–6 are identical to widget mode
154
+
139
155
  ### EOA Wallet Login
140
156
 
157
+ When a signer is provided via `connectEoa`, the exchange client works immediately — no manual agent approval step is needed:
158
+
141
159
  1. User clicks "Wallet" in the `LoginModal`; the `onConnectWallet` callback fires
142
- 2. Your app connects the wallet (e.g., via wagmi) and calls `loginEoa(address)`
143
- 3. The user must approve an agent key before trading:
144
- - Call `approveAgent(signTypedDataAsync)` with the wallet's signing function
145
- - A random agent key pair is generated locally
146
- - The wallet signs an EIP-712 `ApproveAgent` message
147
- - The approval is submitted to Hyperliquid
148
- - The agent key is persisted in localStorage (`hypurr-connect-agent:{address}`)
149
- 4. An `ExchangeClient` is created with `HttpTransport` + `PrivateKeySigner` all exchange actions are signed **client-side** with the agent key
160
+ 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
161
+ 3. The `exchange` client is ready to use right away:
162
+ - **User-signed actions** (transfers, withdrawals, staking) are routed directly to the master wallet the wallet popup appears for the user to approve
163
+ - **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
164
+ - Named agent keys (`"hypurr-connect"`) are cached in localStorage with their `validUntil` timestamp and revalidated against Hyperliquid's `extraAgents` endpoint
165
+ 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`
166
+
167
+ Use the `createEoaSigner` helper to adapt wagmi's `signTypedDataAsync` to the `EoaSigner` interface. Pass a ref to avoid stale closure issues with React hooks:
168
+
169
+ ```tsx
170
+ import { useHypurrConnect, createEoaSigner } from "@hfunlabs/hypurr-connect";
171
+ import { useSignTypedData, useChainId, useAccountEffect } from "wagmi";
172
+
173
+ function ConnectWallet() {
174
+ const { connectEoa, logout, exchange } = useHypurrConnect();
175
+ const { signTypedDataAsync } = useSignTypedData();
176
+ const chainId = useChainId();
177
+
178
+ // Keep a ref so the signer always calls the latest signTypedDataAsync
179
+ const signerRef = useRef(signTypedDataAsync);
180
+ signerRef.current = signTypedDataAsync;
181
+
182
+ useAccountEffect({
183
+ onConnect({ address }) {
184
+ connectEoa(address, createEoaSigner(signerRef, chainId));
185
+ },
186
+ onDisconnect() {
187
+ logout();
188
+ },
189
+ });
190
+
191
+ // exchange is ready — L1 and user-signed actions both work
192
+ if (exchange) {
193
+ // L1 action — agent signs silently (auto-provisioned if needed)
194
+ await exchange.order({ orders: [/* ... */], grouping: "na" });
195
+
196
+ // User-signed action — wallet popup appears
197
+ await exchange.usdSend({ destination: "0x...", amount: "100" });
198
+ }
199
+ }
200
+ ```
201
+
202
+ If you prefer explicit control over agent approval (or don't need user-signed actions), you can omit the signer and call `approveAgent` manually:
203
+
204
+ ```tsx
205
+ connectEoa(address); // no signer — only L1 actions after manual approval
206
+ await approveAgent(signTypedDataAsync, chainId);
207
+ ```
150
208
 
151
209
  ### Using the Exchange Client
152
210
 
153
- Once authenticated (and agent approved for EOA), the `exchange` object from `useHypurrConnect()` is a fully functional `ExchangeClient` from `@hfunlabs/hyperliquid`. Use it for any L1 action:
211
+ 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:
154
212
 
155
213
  ```tsx
156
- const { exchange, agentReady } = useHypurrConnect();
214
+ const { exchange } = useHypurrConnect();
157
215
 
158
- // Place an order (works identically for Telegram and EOA users)
159
216
  if (exchange) {
160
- const result = await exchange.placeOrder({
161
- asset: 0,
162
- isBuy: true,
163
- limitPx: "50000",
164
- sz: "0.001",
165
- orderType: { limit: { tif: "Gtc" } },
217
+ // L1 actions signed by the agent key (no wallet popup)
218
+ await exchange.order({
219
+ orders: [
220
+ {
221
+ a: 0,
222
+ b: true,
223
+ p: "50000",
224
+ s: "0.001",
225
+ t: { limit: { tif: "Gtc" } },
226
+ },
227
+ ],
228
+ grouping: "na",
229
+ });
230
+
231
+ await exchange.cancelOrder({ asset: 0, oid: 12345 });
232
+
233
+ // User-signed actions — signed by the master wallet (wallet popup)
234
+ await exchange.usdSend({ destination: "0x...", amount: "100" });
235
+ await exchange.withdraw({ destination: "0x...", amount: "50" });
236
+ await exchange.spotSend({
237
+ destination: "0x...",
238
+ token: "PURR:0x...",
239
+ amount: "10",
166
240
  });
167
241
  }
168
242
  ```
169
243
 
244
+ The routing is automatic — no need to call different methods or switch signers.
245
+
246
+ ### Multi-Wallet Management (Telegram)
247
+
248
+ 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.
249
+
250
+ ```tsx
251
+ const {
252
+ wallets,
253
+ selectedWalletId,
254
+ selectWallet,
255
+ createWallet,
256
+ deleteWallet,
257
+ refreshWallets,
258
+ } = useHypurrConnect();
259
+
260
+ // List wallets
261
+ wallets.map((w) => (
262
+ <button
263
+ key={w.id}
264
+ onClick={() => selectWallet(w.id)}
265
+ style={{ fontWeight: w.id === selectedWalletId ? "bold" : "normal" }}
266
+ >
267
+ {w.name || w.ethereumAddress}
268
+ </button>
269
+ ));
270
+
271
+ // Create a new wallet
272
+ const newWallet = await createWallet("Trading");
273
+
274
+ // Delete a wallet (auto-selects another if the deleted one was active)
275
+ await deleteWallet(walletId);
276
+ ```
277
+
278
+ #### Wallet Packs & Labels
279
+
280
+ Organize watched wallets into named packs with labels:
281
+
282
+ ```tsx
283
+ const {
284
+ packs,
285
+ createWalletPack,
286
+ addPackLabel,
287
+ modifyPackLabel,
288
+ removePackLabel,
289
+ } = useHypurrConnect();
290
+
291
+ // Create a pack
292
+ const packId = await createWalletPack("Whales");
293
+
294
+ // Add a labeled wallet to the pack
295
+ await addPackLabel({
296
+ walletAddress: "0x...",
297
+ walletLabel: "Big trader",
298
+ packId,
299
+ });
300
+
301
+ // Rename a label
302
+ await modifyPackLabel({
303
+ walletLabelOld: "Big trader",
304
+ walletLabelNew: "Whale #1",
305
+ packId,
306
+ });
307
+
308
+ // Remove a label from the pack
309
+ await removePackLabel({ walletLabel: "Whale #1", packId });
310
+ ```
311
+
312
+ All wallet management functions automatically refresh the wallet list from the server after mutations.
313
+
170
314
  ## API Reference
171
315
 
172
316
  ### Components
@@ -179,7 +323,7 @@ if (exchange) {
179
323
  </HypurrConnectProvider>
180
324
  ```
181
325
 
182
- Context provider that manages all auth state, gRPC clients, exchange clients, and balance tracking. Must wrap any component that uses `useHypurrConnect`.
326
+ Context provider that manages all auth state, gRPC clients, and exchange clients. Must wrap any component that uses `useHypurrConnect`.
183
327
 
184
328
  #### `LoginModal`
185
329
 
@@ -200,31 +344,38 @@ Returns the full auth and exchange state. Throws if used outside `HypurrConnectP
200
344
 
201
345
  ### `HypurrConnectState`
202
346
 
203
- | Property | Type | Description |
204
- | -------------------- | -------------------------------------------------------- | ---------------------------------------------- |
205
- | `user` | `HypurrUser \| null` | Current authenticated user |
206
- | `isLoggedIn` | `boolean` | Whether a user is authenticated |
207
- | `isLoading` | `boolean` | Whether auth is in progress |
208
- | `error` | `string \| null` | Last auth error message |
209
- | `authMethod` | `AuthMethod` | `"telegram"`, `"eoa"`, or `null` |
210
- | `exchange` | `ExchangeClient \| null` | Hyperliquid exchange client for L1 actions |
211
- | `usdcBalance` | `string \| null` | User's USDC balance from the clearinghouse |
212
- | `usdcBalanceLoading` | `boolean` | Whether balance is being fetched |
213
- | `refreshBalance` | `() => void` | Manually re-fetch USDC balance |
214
- | `loginModalOpen` | `boolean` | Whether the login modal is visible |
215
- | `openLoginModal` | `() => void` | Show the login modal |
216
- | `closeLoginModal` | `() => void` | Hide the login modal |
217
- | `loginTelegram` | `(data: TelegramLoginData) => void` | Authenticate with Telegram OAuth data |
218
- | `loginEoa` | `(address: \`0x\${string}\`) => void` | Authenticate with an EOA wallet address |
219
- | `logout` | `() => void` | Clear all auth state and localStorage |
220
- | `agent` | `StoredAgent \| null` | Current agent key (EOA flow only) |
221
- | `agentReady` | `boolean` | Whether the agent key is approved and ready |
222
- | `approveAgent` | `(signTypedDataAsync: SignTypedDataFn) => Promise<void>` | Generate and approve a new agent key |
223
- | `clearAgent` | `() => void` | Remove the agent key from state and storage |
224
- | `botId` | `string` | Telegram bot ID from config |
225
- | `authDataMap` | `Record<string, string>` | Raw Telegram auth data as key-value pairs |
226
- | `telegramClient` | `TelegramClient` | Low-level gRPC client for the Telegram service |
227
- | `staticClient` | `StaticClient` | Low-level gRPC client for the Static service |
347
+ | Property | Type | Description |
348
+ | ------------------ | ------------------------------------------------------------------------- | ---------------------------------------------------------------- |
349
+ | `user` | `HypurrUser \| null` | Current authenticated user (reflects selected wallet) |
350
+ | `isLoggedIn` | `boolean` | Whether a user is authenticated |
351
+ | `isLoading` | `boolean` | Whether auth is in progress |
352
+ | `error` | `string \| null` | Last auth or dead-agent error message |
353
+ | `authMethod` | `AuthMethod` | `"telegram"`, `"eoa"`, or `null` |
354
+ | `exchange` | `ExchangeClient \| null` | Hyperliquid exchange client (L1 + user-signed actions for EOA) |
355
+ | `wallets` | `HyperliquidWallet[]` | All wallets for the Telegram user (empty for EOA) |
356
+ | `selectedWalletId` | `number` | ID of the currently active wallet |
357
+ | `selectWallet` | `(walletId: number) => void` | Switch the active wallet |
358
+ | `createWallet` | `(name: string) => Promise<HyperliquidWallet>` | Create a new wallet (Telegram only) |
359
+ | `deleteWallet` | `(walletId: number) => Promise<void>` | Delete a wallet (Telegram only) |
360
+ | `refreshWallets` | `() => void` | Re-fetch wallets and packs from the server |
361
+ | `packs` | `TelegramChatWalletPack[]` | Wallet packs for the Telegram user |
362
+ | `createWalletPack` | `(name: string) => Promise<number>` | Create a wallet pack; returns the new pack ID |
363
+ | `addPackLabel` | `(params) => Promise<void>` | Add a labeled wallet to a pack |
364
+ | `modifyPackLabel` | `(params) => Promise<void>` | Rename a label within a pack |
365
+ | `removePackLabel` | `(params) => Promise<void>` | Remove a label from a pack |
366
+ | `loginModalOpen` | `boolean` | Whether the login modal is visible |
367
+ | `openLoginModal` | `() => void` | Show the login modal |
368
+ | `closeLoginModal` | `() => void` | Hide the login modal |
369
+ | `connectEoa` | `(address: \`0x\${string}\`, signer?: EoaSigner) => void` | Connect EOA wallet (sync); pass signer to enable user-signed actions and auto-provisioning |
370
+ | `approveAgent` | `(signTypedDataAsync: SignTypedDataFn, chainId: number) => Promise<void>` | Approve a named agent key (async, triggers wallet prompt) |
371
+ | `logout` | `() => void` | Clear all auth state and localStorage |
372
+ | `agent` | `StoredAgent \| null` | Current agent key (EOA flow only) |
373
+ | `agentReady` | `boolean` | Whether the exchange client can sign (true for TG, or EOA+agent) |
374
+ | `clearAgent` | `() => void` | Remove the agent key from state and storage |
375
+ | `botId` | `string` | Telegram bot ID from config |
376
+ | `authDataMap` | `Record<string, string>` | Raw Telegram auth data as key-value pairs |
377
+ | `telegramClient` | `TelegramClient` | Low-level gRPC client for the Telegram service |
378
+ | `staticClient` | `StaticClient` | Low-level gRPC client for the Static service |
228
379
 
229
380
  ### Types
230
381
 
@@ -268,9 +419,62 @@ interface StoredAgent {
268
419
  privateKey: `0x${string}`;
269
420
  address: `0x${string}`;
270
421
  approvedAt: number; // Timestamp when approved on-chain
422
+ validUntil: number; // Epoch ms from extraAgents; agent is invalid after this
271
423
  }
272
424
  ```
273
425
 
426
+ #### `HyperliquidWallet`
427
+
428
+ Re-exported from `hypurr-grpc`:
429
+
430
+ ```typescript
431
+ interface HyperliquidWallet {
432
+ id: number;
433
+ name: string;
434
+ ethereumAddress: string;
435
+ isAgent: boolean;
436
+ isReadOnly: boolean;
437
+ // ... plus balances, movements, sessions
438
+ }
439
+ ```
440
+
441
+ #### `TelegramChatWalletPack`
442
+
443
+ Re-exported from `hypurr-grpc`:
444
+
445
+ ```typescript
446
+ interface TelegramChatWalletPack {
447
+ id: number;
448
+ telegramChatId: number;
449
+ name: string;
450
+ }
451
+ ```
452
+
453
+ #### `EoaSigner`
454
+
455
+ Encapsulates the master wallet's EIP-712 signing function and chain ID. Pass to `connectEoa` to enable user-signed actions and auto agent provisioning.
456
+
457
+ ```typescript
458
+ interface EoaSigner {
459
+ signTypedData: SignTypedDataFn;
460
+ chainId: number;
461
+ }
462
+ ```
463
+
464
+ #### `createEoaSigner(signTypedData, chainId): EoaSigner`
465
+
466
+ 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:
467
+
468
+ ```typescript
469
+ // With a ref (recommended for React — always calls the latest function)
470
+ const signerRef = useRef(signTypedDataAsync);
471
+ signerRef.current = signTypedDataAsync;
472
+ const signer = createEoaSigner(signerRef, chainId);
473
+
474
+ // With a direct function (fine for stable references)
475
+ const signer = createEoaSigner(signTypedDataAsync, chainId);
476
+ ```
477
+
274
478
  #### `SignTypedDataFn`
275
479
 
276
480
  ```typescript
@@ -334,28 +538,39 @@ Both use `GrpcWebFetchTransport` with the configured `baseUrl`, `timeout`, and `
334
538
  ## Architecture
335
539
 
336
540
  ```
337
- ┌─────────────────────────────────────────────────────────┐
338
- HypurrConnectProvider
339
-
340
- ┌───────────────┐ ┌────────────────────────┐
341
- │ │ Telegram Auth │──gRPC──▶ │ GrpcExchangeTransport
342
- │ │ (server- ┌──────────────────┐
343
- │ │ signed) │ │ │ telegramClient
344
- └───────────────┘ │ .hyperliquidCore │ │ │
345
- │ Action() │ │ │
346
- ┌───────────────┐ └──────────────────┘ │ │
347
- │ │ EOA Auth │──HTTP──▶ │ HttpTransport
348
- │ │ (agent key + PrivateKeySigner
349
- │ │ signed) │ └────────────────────────┘ │
350
- └───────────────┘
351
-
352
- ┌──────────────┐
353
- │ │ExchangeClient
354
- └──────────────┘
355
-
356
-
357
- Hyperliquid L1
358
- └─────────────────────────────────────────────────────────┘
541
+ ┌──────────────────────────────────────────────────────────────────┐
542
+ HypurrConnectProvider
543
+
544
+ ┌──────────────────┐ ┌──────────────────────────────┐
545
+ │ │ Telegram Auth │─gRPC─▶│ GrpcExchangeTransport
546
+ │ │ (server-signed) telegramClient
547
+ └──────────────────┘ .hyperliquidCoreAction() │
548
+ └──────────────────────────────┘
549
+
550
+ ┌──────────────────┐ ┌──────────────────────────────┐
551
+ │ │ EOA Auth │─HTTP─▶│ HttpTransport + Dual Wallet
552
+ │ │ (client-signed)
553
+ └──────────────────┘ signTypedData(params) ─┐ │
554
+ │ │
555
+ domain = "Exchange" │ │ │
556
+ │ │ └─▶ Agent Key │ │ │
557
+ │ │ (auto-provision
558
+ │ │ if needed) │ │ │
559
+ │ │
560
+ domain = "Hyperliquid │ │ │
561
+ │ │ SignTransaction" │ │ │
562
+ │ │ └─▶ Master Wallet │ │ │
563
+ │ │ (wallet popup) │ │ │
564
+ │ └──────────────────────────────┘ │
565
+ │ │ │
566
+ │ ▼ │
567
+ │ ┌──────────────┐ │
568
+ │ │ExchangeClient│ │
569
+ │ └──────────────┘ │
570
+ │ │ │
571
+ │ ▼ │
572
+ │ Hyperliquid L1 │
573
+ └──────────────────────────────────────────────────────────────────┘
359
574
  ```
360
575
 
361
576
  ## License
package/dist/index.d.ts CHANGED
@@ -3,13 +3,18 @@ import { ReactNode } from 'react';
3
3
  import { ExchangeClient, IRequestTransport } from '@hfunlabs/hyperliquid';
4
4
  import { StaticClient } from 'hypurr-grpc/ts/hypurr/static/static_service.client';
5
5
  import { TelegramClient } from 'hypurr-grpc/ts/hypurr/telegram/telegram_service.client';
6
+ import { TelegramChatWalletPack } from 'hypurr-grpc/ts/hypurr/user';
7
+ export { TelegramChatWalletPack } from 'hypurr-grpc/ts/hypurr/user';
8
+ import { HyperliquidWallet } from 'hypurr-grpc/ts/hypurr/wallet';
9
+ export { HyperliquidWallet } from 'hypurr-grpc/ts/hypurr/wallet';
6
10
 
7
11
  interface HypurrConnectConfig {
8
12
  grpcTimeout?: number;
9
13
  isTestnet?: boolean;
10
- telegram?: {
14
+ telegram: {
11
15
  botUsername: string;
12
- botId: string;
16
+ botId?: string;
17
+ useWidget: boolean;
13
18
  };
14
19
  }
15
20
  interface TelegramLoginData {
@@ -29,11 +34,15 @@ interface HypurrUser {
29
34
  photoUrl?: string;
30
35
  authMethod: AuthMethod;
31
36
  telegramId?: string;
37
+ hfunScore?: number;
38
+ reputationScore?: number;
32
39
  }
33
40
  interface StoredAgent {
34
41
  privateKey: `0x${string}`;
35
42
  address: `0x${string}`;
36
43
  approvedAt: number;
44
+ /** Epoch ms from the `extraAgents` response; agent is invalid after this time. */
45
+ validUntil: number;
37
46
  }
38
47
  type SignTypedDataFn = (params: {
39
48
  domain: Record<string, unknown>;
@@ -44,6 +53,37 @@ type SignTypedDataFn = (params: {
44
53
  primaryType: string;
45
54
  message: Record<string, unknown>;
46
55
  }) => Promise<`0x${string}`>;
56
+ /** Wallet signer provided at EOA connect time for user-signed actions. */
57
+ interface EoaSigner {
58
+ signTypedData: SignTypedDataFn;
59
+ chainId: number;
60
+ }
61
+ /**
62
+ * Create an {@link EoaSigner} from any EIP-712 signing function.
63
+ *
64
+ * Accepts either a direct function or a `{ current }` ref object so the
65
+ * signer always calls through to the latest function (avoids stale closures
66
+ * with React hooks like wagmi's `useSignTypedData`).
67
+ *
68
+ * @example wagmi v2 — ref pattern (recommended)
69
+ * ```ts
70
+ * const { signTypedDataAsync } = useSignTypedData();
71
+ * const chainId = useChainId();
72
+ * const signerRef = useRef(signTypedDataAsync);
73
+ * signerRef.current = signTypedDataAsync; // stays fresh every render
74
+ *
75
+ * // call once — the ref keeps it up to date
76
+ * connectEoa(address, createEoaSigner(signerRef, chainId));
77
+ * ```
78
+ *
79
+ * @example direct function (e.g. from viem WalletClient)
80
+ * ```ts
81
+ * connectEoa(address, createEoaSigner(client.signTypedData, chainId));
82
+ * ```
83
+ */
84
+ declare function createEoaSigner(signTypedDataAsync: ((args: Record<string, unknown>) => Promise<`0x${string}`>) | {
85
+ current: (args: Record<string, unknown>) => Promise<`0x${string}`>;
86
+ }, chainId: number): EoaSigner;
47
87
  interface HypurrConnectState {
48
88
  user: HypurrUser | null;
49
89
  isLoggedIn: boolean;
@@ -51,18 +91,36 @@ interface HypurrConnectState {
51
91
  error: string | null;
52
92
  authMethod: AuthMethod;
53
93
  exchange: ExchangeClient<any> | null;
54
- usdcBalance: string | null;
55
- usdcBalanceLoading: boolean;
56
- refreshBalance: () => void;
94
+ wallets: HyperliquidWallet[];
95
+ selectedWalletId: number;
96
+ selectWallet: (walletId: number) => void;
97
+ createWallet: (name: string) => Promise<HyperliquidWallet>;
98
+ deleteWallet: (walletId: number) => Promise<void>;
99
+ refreshWallets: () => void;
100
+ packs: TelegramChatWalletPack[];
101
+ createWalletPack: (name: string) => Promise<number>;
102
+ addPackLabel: (params: {
103
+ walletAddress: string;
104
+ walletLabel: string;
105
+ packId: number;
106
+ }) => Promise<void>;
107
+ modifyPackLabel: (params: {
108
+ walletLabelOld: string;
109
+ walletLabelNew: string;
110
+ packId: number;
111
+ }) => Promise<void>;
112
+ removePackLabel: (params: {
113
+ walletLabel: string;
114
+ packId: number;
115
+ }) => Promise<void>;
57
116
  loginModalOpen: boolean;
58
117
  openLoginModal: () => void;
59
118
  closeLoginModal: () => void;
60
- loginTelegram: (data: TelegramLoginData) => void;
61
- loginEoa: (address: `0x${string}`) => void;
119
+ connectEoa: (address: `0x${string}`, signer?: EoaSigner) => void;
120
+ approveAgent: (signTypedDataAsync: SignTypedDataFn, chainId: number) => Promise<void>;
62
121
  logout: () => void;
63
122
  agent: StoredAgent | null;
64
123
  agentReady: boolean;
65
- approveAgent: (signTypedDataAsync: SignTypedDataFn) => Promise<void>;
66
124
  clearAgent: () => void;
67
125
  botId: string;
68
126
  authDataMap: Record<string, string>;
@@ -82,6 +140,16 @@ interface LoginModalProps {
82
140
  }
83
141
  declare function LoginModal({ onConnectWallet, walletIcon }: LoginModalProps): react_jsx_runtime.JSX.Element;
84
142
 
143
+ interface TelegramLoginWidgetProps {
144
+ botUsername: string;
145
+ onAuth: (data: TelegramLoginData) => void;
146
+ buttonSize?: "large" | "medium" | "small";
147
+ cornerRadius?: number;
148
+ showUserPhoto?: boolean;
149
+ requestAccess?: boolean;
150
+ }
151
+ declare function TelegramLoginWidget({ botUsername, onAuth, buttonSize, cornerRadius, showUserPhoto, requestAccess, }: TelegramLoginWidgetProps): react_jsx_runtime.JSX.Element;
152
+
85
153
  interface GrpcExchangeTransportConfig {
86
154
  isTestnet?: boolean;
87
155
  telegramClient: TelegramClient;
@@ -110,4 +178,4 @@ declare class GrpcExchangeTransport implements IRequestTransport {
110
178
  declare function createTelegramClient(config: HypurrConnectConfig): TelegramClient;
111
179
  declare function createStaticClient(config: HypurrConnectConfig): StaticClient;
112
180
 
113
- export { type AuthMethod, GrpcExchangeTransport, type GrpcExchangeTransportConfig, type HypurrConnectConfig, HypurrConnectProvider, type HypurrConnectState, type HypurrUser, LoginModal, type LoginModalProps, type SignTypedDataFn, type StoredAgent, type TelegramLoginData, createStaticClient, createTelegramClient, useHypurrConnect };
181
+ export { type AuthMethod, type EoaSigner, GrpcExchangeTransport, type GrpcExchangeTransportConfig, type HypurrConnectConfig, HypurrConnectProvider, type HypurrConnectState, type HypurrUser, LoginModal, type LoginModalProps, type SignTypedDataFn, type StoredAgent, type TelegramLoginData, TelegramLoginWidget, type TelegramLoginWidgetProps, createEoaSigner, createStaticClient, createTelegramClient, useHypurrConnect };