@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 +294 -79
- package/dist/index.d.ts +77 -9
- package/dist/index.js +545 -164
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/HypurrConnectProvider.tsx +557 -164
- package/src/LoginModal.tsx +21 -7
- package/src/TelegramLoginWidget.tsx +62 -0
- package/src/agent.ts +80 -0
- package/src/index.ts +8 -0
- package/src/types.ts +89 -11
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
|
|
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
|
|
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** —
|
|
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,
|
|
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
|
|
117
|
-
botId
|
|
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
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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 `
|
|
143
|
-
3. The
|
|
144
|
-
-
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
|
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
|
|
214
|
+
const { exchange } = useHypurrConnect();
|
|
157
215
|
|
|
158
|
-
// Place an order (works identically for Telegram and EOA users)
|
|
159
216
|
if (exchange) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
|
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
|
|
204
|
-
|
|
|
205
|
-
| `user`
|
|
206
|
-
| `isLoggedIn`
|
|
207
|
-
| `isLoading`
|
|
208
|
-
| `error`
|
|
209
|
-
| `authMethod`
|
|
210
|
-
| `exchange`
|
|
211
|
-
| `
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
218
|
-
| `
|
|
219
|
-
| `
|
|
220
|
-
| `
|
|
221
|
-
| `
|
|
222
|
-
| `
|
|
223
|
-
| `
|
|
224
|
-
| `
|
|
225
|
-
| `
|
|
226
|
-
| `
|
|
227
|
-
| `
|
|
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
|
-
│
|
|
339
|
-
│
|
|
340
|
-
│
|
|
341
|
-
│ │
|
|
342
|
-
│ │ (server-
|
|
343
|
-
│ │
|
|
344
|
-
│
|
|
345
|
-
│
|
|
346
|
-
│
|
|
347
|
-
│ │
|
|
348
|
-
│ │ (
|
|
349
|
-
│ │
|
|
350
|
-
│
|
|
351
|
-
│
|
|
352
|
-
│
|
|
353
|
-
│ │
|
|
354
|
-
│
|
|
355
|
-
│
|
|
356
|
-
│
|
|
357
|
-
│
|
|
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
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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 };
|