@hfunlabs/hypurr-connect 0.1.8 → 0.1.10
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 +80 -64
- package/dist/index.d.ts +23 -15
- package/dist/index.js +361 -254
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/GrpcExchangeTransport.ts +30 -9
- package/src/HypurrConnectProvider.tsx +366 -125
- package/src/LoginModal.tsx +15 -74
- package/src/grpc.ts +2 -2
- package/src/index.ts +0 -2
- package/src/types.ts +27 -5
|
@@ -3,7 +3,11 @@ import {
|
|
|
3
3
|
HttpTransport,
|
|
4
4
|
type IRequestTransport,
|
|
5
5
|
} from "@hfunlabs/hyperliquid";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
PrivateKeySigner,
|
|
8
|
+
signUserSignedAction,
|
|
9
|
+
} from "@hfunlabs/hyperliquid/signing";
|
|
10
|
+
import type { RpcOptions } from "@protobuf-ts/runtime-rpc";
|
|
7
11
|
import type { TelegramUserResponse } from "hypurr-grpc/ts/hypurr/telegram/telegram_service";
|
|
8
12
|
import type {
|
|
9
13
|
TelegramUser as HypurrTelegramUser,
|
|
@@ -40,29 +44,87 @@ import type {
|
|
|
40
44
|
HypurrUser,
|
|
41
45
|
SignTypedDataFn,
|
|
42
46
|
StoredAgent,
|
|
43
|
-
TelegramLoginData,
|
|
44
47
|
} from "./types";
|
|
45
48
|
|
|
46
49
|
/** @internal context value — extends the public type with fields used only by library internals */
|
|
47
50
|
interface InternalConnectState extends HypurrConnectState {
|
|
48
|
-
loginTelegram: (
|
|
51
|
+
loginTelegram: () => void;
|
|
49
52
|
botUsername: string;
|
|
50
53
|
useWidget: boolean;
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
const TELEGRAM_STORAGE_KEY = "hypurr-connect-tg-
|
|
56
|
+
const TELEGRAM_STORAGE_KEY = "hypurr-connect-tg-jwt";
|
|
57
|
+
const LEGACY_TELEGRAM_STORAGE_KEY = "hypurr-connect-tg-user";
|
|
58
|
+
const TELEGRAM_AUTH_STATE_KEY = "hypurr-connect-auth-state";
|
|
59
|
+
const TELEGRAM_AUTH_MESSAGE = "hypurr-connect:telegram-auth";
|
|
60
|
+
const DEFAULT_AUTH_HUB_URL = "https://127.0.0.1:443/login";
|
|
61
|
+
const DEFAULT_TELEGRAM_SCOPES = [
|
|
62
|
+
"telegram:user:read",
|
|
63
|
+
"telegram:wallet:read",
|
|
64
|
+
"telegram:wallet:write",
|
|
65
|
+
"telegram:trade:read",
|
|
66
|
+
"telegram:trade:write",
|
|
67
|
+
"telegram:cabal:read",
|
|
68
|
+
"telegram:cabal:write",
|
|
69
|
+
"telegram:agent:write",
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
function isInvalidTelegramAuthError(err: unknown): boolean {
|
|
73
|
+
const msg =
|
|
74
|
+
err instanceof Error
|
|
75
|
+
? err.message
|
|
76
|
+
: typeof err === "object" && err !== null && "message" in err
|
|
77
|
+
? String((err as { message: unknown }).message)
|
|
78
|
+
: String(err);
|
|
79
|
+
return /invalid telegram auth data|invalid auth token|missing authorization token/i.test(
|
|
80
|
+
msg,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
54
83
|
|
|
55
|
-
function
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
84
|
+
function currentReturnTo(): string {
|
|
85
|
+
const url = new URL(window.location.href);
|
|
86
|
+
for (const param of [
|
|
87
|
+
"token",
|
|
88
|
+
"token_type",
|
|
89
|
+
"token_source",
|
|
90
|
+
"state",
|
|
91
|
+
"scope",
|
|
92
|
+
]) {
|
|
93
|
+
url.searchParams.delete(param);
|
|
94
|
+
}
|
|
95
|
+
return url.toString();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function randomState(): string {
|
|
99
|
+
const bytes = new Uint8Array(16);
|
|
100
|
+
crypto.getRandomValues(bytes);
|
|
101
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
102
|
+
"",
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function normalizeScopes(scope?: string | string[]): string {
|
|
107
|
+
if (Array.isArray(scope)) return scope.join(" ");
|
|
108
|
+
return scope?.trim() || DEFAULT_TELEGRAM_SCOPES.join(" ");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isTelegramAuthMessage(
|
|
112
|
+
data: unknown,
|
|
113
|
+
): data is {
|
|
114
|
+
type: typeof TELEGRAM_AUTH_MESSAGE;
|
|
115
|
+
token: string;
|
|
116
|
+
state: string;
|
|
117
|
+
} {
|
|
118
|
+
return (
|
|
119
|
+
typeof data === "object" &&
|
|
120
|
+
data !== null &&
|
|
121
|
+
"type" in data &&
|
|
122
|
+
"token" in data &&
|
|
123
|
+
"state" in data &&
|
|
124
|
+
(data as { type: unknown }).type === TELEGRAM_AUTH_MESSAGE &&
|
|
125
|
+
typeof (data as { token: unknown }).token === "string" &&
|
|
126
|
+
typeof (data as { state: unknown }).state === "string"
|
|
127
|
+
);
|
|
66
128
|
}
|
|
67
129
|
|
|
68
130
|
const HypurrConnectContext = createContext<InternalConnectState | null>(null);
|
|
@@ -97,40 +159,124 @@ export function HypurrConnectProvider({
|
|
|
97
159
|
const staticClient = useMemo(() => createStaticClient(config), [config]);
|
|
98
160
|
|
|
99
161
|
// ── Telegram auth state ──────────────────────────────────────
|
|
100
|
-
const [
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
);
|
|
162
|
+
const [tgAuthToken, setTgAuthToken] = useState<string | null>(() => {
|
|
163
|
+
try {
|
|
164
|
+
return localStorage.getItem(TELEGRAM_STORAGE_KEY);
|
|
165
|
+
} catch {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
110
169
|
const [tgUser, setTgUser] = useState<HypurrTelegramUser | null>(null);
|
|
111
170
|
const [tgLoading, setTgLoading] = useState(false);
|
|
112
171
|
const [tgError, setTgError] = useState<string | null>(null);
|
|
113
172
|
|
|
114
|
-
const authDataMap = useMemo(
|
|
115
|
-
|
|
116
|
-
|
|
173
|
+
const authDataMap = useMemo(() => ({}), []);
|
|
174
|
+
const telegramRpcOptions = useMemo<RpcOptions | undefined>(
|
|
175
|
+
() =>
|
|
176
|
+
tgAuthToken
|
|
177
|
+
? { meta: { authorization: `Bearer ${tgAuthToken}` } }
|
|
178
|
+
: undefined,
|
|
179
|
+
[tgAuthToken],
|
|
117
180
|
);
|
|
118
181
|
|
|
119
182
|
const [tgUserTick, setTgUserTick] = useState(0);
|
|
120
183
|
|
|
184
|
+
// Auto-disconnect when the server rejects telegram auth data.
|
|
185
|
+
const onInvalidAuthRef = useRef<(() => void) | null>(null);
|
|
186
|
+
onInvalidAuthRef.current = () => {
|
|
187
|
+
console.warn(
|
|
188
|
+
"[HypurrConnect] Invalid Telegram auth token — disconnecting.",
|
|
189
|
+
);
|
|
190
|
+
setTgAuthToken(null);
|
|
191
|
+
setTgUser(null);
|
|
192
|
+
setTgError(null);
|
|
193
|
+
localStorage.removeItem(TELEGRAM_STORAGE_KEY);
|
|
194
|
+
localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const acceptTelegramToken = useCallback((token: string) => {
|
|
198
|
+
setTgAuthToken(token);
|
|
199
|
+
setTgError(null);
|
|
200
|
+
localStorage.setItem(TELEGRAM_STORAGE_KEY, token);
|
|
201
|
+
localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
|
|
202
|
+
}, []);
|
|
203
|
+
|
|
121
204
|
useEffect(() => {
|
|
122
|
-
|
|
205
|
+
const params = new URLSearchParams(window.location.search);
|
|
206
|
+
const token = params.get("token");
|
|
207
|
+
if (!token) {
|
|
208
|
+
localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const callbackState = params.get("state") ?? "";
|
|
213
|
+
|
|
214
|
+
if (window.opener && window.opener !== window) {
|
|
215
|
+
window.opener.postMessage(
|
|
216
|
+
{
|
|
217
|
+
type: TELEGRAM_AUTH_MESSAGE,
|
|
218
|
+
token,
|
|
219
|
+
state: callbackState,
|
|
220
|
+
},
|
|
221
|
+
window.location.origin,
|
|
222
|
+
);
|
|
223
|
+
window.close();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const expectedState = sessionStorage.getItem(TELEGRAM_AUTH_STATE_KEY);
|
|
228
|
+
sessionStorage.removeItem(TELEGRAM_AUTH_STATE_KEY);
|
|
229
|
+
if (!expectedState || callbackState !== expectedState) {
|
|
230
|
+
setTgError("Invalid auth callback state.");
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
acceptTelegramToken(token);
|
|
235
|
+
|
|
236
|
+
const cleanUrl = new URL(window.location.href);
|
|
237
|
+
for (const param of [
|
|
238
|
+
"token",
|
|
239
|
+
"token_type",
|
|
240
|
+
"token_source",
|
|
241
|
+
"state",
|
|
242
|
+
"scope",
|
|
243
|
+
]) {
|
|
244
|
+
cleanUrl.searchParams.delete(param);
|
|
245
|
+
}
|
|
246
|
+
window.history.replaceState({}, document.title, cleanUrl.toString());
|
|
247
|
+
}, [acceptTelegramToken]);
|
|
248
|
+
|
|
249
|
+
useEffect(() => {
|
|
250
|
+
function onMessage(event: MessageEvent) {
|
|
251
|
+
if (event.origin !== window.location.origin) return;
|
|
252
|
+
if (!isTelegramAuthMessage(event.data)) return;
|
|
253
|
+
|
|
254
|
+
const expectedState = sessionStorage.getItem(TELEGRAM_AUTH_STATE_KEY);
|
|
255
|
+
sessionStorage.removeItem(TELEGRAM_AUTH_STATE_KEY);
|
|
256
|
+
if (!expectedState || event.data.state !== expectedState) {
|
|
257
|
+
setTgError("Invalid auth callback state.");
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
acceptTelegramToken(event.data.token);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
window.addEventListener("message", onMessage);
|
|
265
|
+
return () => window.removeEventListener("message", onMessage);
|
|
266
|
+
}, [acceptTelegramToken]);
|
|
267
|
+
|
|
268
|
+
useEffect(() => {
|
|
269
|
+
if (!tgAuthToken || !telegramRpcOptions) return;
|
|
123
270
|
let cancelled = false;
|
|
124
271
|
setTgLoading(true);
|
|
125
272
|
setTgError(null);
|
|
126
273
|
|
|
127
274
|
(async () => {
|
|
128
275
|
try {
|
|
129
|
-
const authData = toAuthDataMap(tgLoginData);
|
|
130
276
|
const [{ response: userResp }, { response: walletsResp }] =
|
|
131
277
|
await Promise.all([
|
|
132
|
-
tgClient.telegramUser({ authData }),
|
|
133
|
-
tgClient.telegramUserWallets({ authData }),
|
|
278
|
+
tgClient.telegramUser({ authData: {} }, telegramRpcOptions),
|
|
279
|
+
tgClient.telegramUserWallets({ authData: {} }, telegramRpcOptions),
|
|
134
280
|
]);
|
|
135
281
|
if (cancelled) return;
|
|
136
282
|
const user = (userResp as TelegramUserResponse).user ?? null;
|
|
@@ -142,6 +288,10 @@ export function HypurrConnectProvider({
|
|
|
142
288
|
setTgUser(user);
|
|
143
289
|
} catch (err) {
|
|
144
290
|
if (cancelled) return;
|
|
291
|
+
if (isInvalidTelegramAuthError(err)) {
|
|
292
|
+
onInvalidAuthRef.current?.();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
145
295
|
console.error("[HypurrConnect] gRPC TelegramUser failed:", err);
|
|
146
296
|
setTgError(err instanceof Error ? err.message : String(err));
|
|
147
297
|
} finally {
|
|
@@ -152,7 +302,7 @@ export function HypurrConnectProvider({
|
|
|
152
302
|
return () => {
|
|
153
303
|
cancelled = true;
|
|
154
304
|
};
|
|
155
|
-
}, [
|
|
305
|
+
}, [tgAuthToken, telegramRpcOptions, tgClient, tgUserTick]);
|
|
156
306
|
|
|
157
307
|
// ── EOA auth state ───────────────────────────────────────────
|
|
158
308
|
const [eoaAddress, setEoaAddress] = useState<`0x${string}` | null>(null);
|
|
@@ -162,7 +312,7 @@ export function HypurrConnectProvider({
|
|
|
162
312
|
const eoaSignerRef = useRef<EoaSigner | null>(null);
|
|
163
313
|
|
|
164
314
|
// ── Derived auth ─────────────────────────────────────────────
|
|
165
|
-
const authMethod: AuthMethod =
|
|
315
|
+
const authMethod: AuthMethod = tgAuthToken
|
|
166
316
|
? "telegram"
|
|
167
317
|
: eoaAddress
|
|
168
318
|
? "eoa"
|
|
@@ -220,14 +370,20 @@ export function HypurrConnectProvider({
|
|
|
220
370
|
try {
|
|
221
371
|
const [{ response: twapResp }, { response: scaleResp }] =
|
|
222
372
|
await Promise.all([
|
|
223
|
-
tgClient.hyperliquidWalletTwapSessions(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
373
|
+
tgClient.hyperliquidWalletTwapSessions(
|
|
374
|
+
{
|
|
375
|
+
authData: {},
|
|
376
|
+
walletId: selectedWalletId,
|
|
377
|
+
},
|
|
378
|
+
telegramRpcOptions,
|
|
379
|
+
),
|
|
380
|
+
tgClient.hyperliquidWalletScaleSessions(
|
|
381
|
+
{
|
|
382
|
+
authData: {},
|
|
383
|
+
walletId: selectedWalletId,
|
|
384
|
+
},
|
|
385
|
+
telegramRpcOptions,
|
|
386
|
+
),
|
|
231
387
|
]);
|
|
232
388
|
if (cancelled) return;
|
|
233
389
|
setWallets((prev) =>
|
|
@@ -241,7 +397,11 @@ export function HypurrConnectProvider({
|
|
|
241
397
|
: w,
|
|
242
398
|
),
|
|
243
399
|
);
|
|
244
|
-
} catch {
|
|
400
|
+
} catch (err) {
|
|
401
|
+
if (isInvalidTelegramAuthError(err)) {
|
|
402
|
+
onInvalidAuthRef.current?.();
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
245
405
|
// Silently ignore poll errors — next tick will retry.
|
|
246
406
|
}
|
|
247
407
|
};
|
|
@@ -253,19 +413,24 @@ export function HypurrConnectProvider({
|
|
|
253
413
|
cancelled = true;
|
|
254
414
|
clearInterval(id);
|
|
255
415
|
};
|
|
256
|
-
}, [
|
|
416
|
+
}, [
|
|
417
|
+
authMethod,
|
|
418
|
+
selectedWalletId,
|
|
419
|
+
pollInterval,
|
|
420
|
+
tgClient,
|
|
421
|
+
telegramRpcOptions,
|
|
422
|
+
]);
|
|
257
423
|
|
|
258
424
|
const user = useMemo<HypurrUser | null>(() => {
|
|
259
|
-
if (
|
|
425
|
+
if (tgAuthToken && authMethod === "telegram" && selectedWallet && tgUser) {
|
|
260
426
|
return {
|
|
261
427
|
address: selectedWallet.ethereumAddress,
|
|
262
428
|
walletId: selectedWallet.id,
|
|
263
|
-
displayName:
|
|
264
|
-
? `@${
|
|
265
|
-
:
|
|
266
|
-
photoUrl: tgLoginData.photo_url,
|
|
429
|
+
displayName: tgUser.telegramUsername
|
|
430
|
+
? `@${tgUser.telegramUsername}`
|
|
431
|
+
: `Telegram ${tgUser.telegramId}`,
|
|
267
432
|
authMethod: "telegram",
|
|
268
|
-
telegramId: String(
|
|
433
|
+
telegramId: String(tgUser.telegramId),
|
|
269
434
|
hfunScore: tgUser?.reputation?.hfunScore,
|
|
270
435
|
reputationScore: tgUser?.reputation?.reputationScore,
|
|
271
436
|
};
|
|
@@ -279,7 +444,7 @@ export function HypurrConnectProvider({
|
|
|
279
444
|
};
|
|
280
445
|
}
|
|
281
446
|
return null;
|
|
282
|
-
}, [
|
|
447
|
+
}, [tgAuthToken, selectedWallet, eoaAddress, authMethod, tgUser]);
|
|
283
448
|
|
|
284
449
|
// ── Exchange client ──────────────────────────────────────────
|
|
285
450
|
// Telegram: GrpcExchangeTransport → HyperliquidCoreAction (server signs)
|
|
@@ -322,8 +487,9 @@ export function HypurrConnectProvider({
|
|
|
322
487
|
const transport = new GrpcExchangeTransport({
|
|
323
488
|
isTestnet: config.isTestnet ?? false,
|
|
324
489
|
telegramClient: tgClient,
|
|
325
|
-
|
|
490
|
+
rpcOptions: telegramRpcOptions,
|
|
326
491
|
walletId: user.walletId,
|
|
492
|
+
onAuthError: () => onInvalidAuthRef.current?.(),
|
|
327
493
|
});
|
|
328
494
|
return new ExchangeClient({
|
|
329
495
|
transport,
|
|
@@ -406,7 +572,8 @@ export function HypurrConnectProvider({
|
|
|
406
572
|
const { privateKey, address: agentAddress } =
|
|
407
573
|
await generateAgentKey();
|
|
408
574
|
|
|
409
|
-
const chainIdHex =
|
|
575
|
+
const chainIdHex =
|
|
576
|
+
`0x${signer.chainId.toString(16)}` as `0x${string}`;
|
|
410
577
|
const nonce = Date.now();
|
|
411
578
|
const action = {
|
|
412
579
|
type: "approveAgent" as const,
|
|
@@ -538,7 +705,7 @@ export function HypurrConnectProvider({
|
|
|
538
705
|
eoaAddress,
|
|
539
706
|
config.isTestnet,
|
|
540
707
|
tgClient,
|
|
541
|
-
|
|
708
|
+
telegramRpcOptions,
|
|
542
709
|
]);
|
|
543
710
|
|
|
544
711
|
const handleClearAgent = useCallback(() => {
|
|
@@ -551,43 +718,52 @@ export function HypurrConnectProvider({
|
|
|
551
718
|
// ── Wallet management (Telegram only) ───────────────────────
|
|
552
719
|
const createWallet = useCallback(
|
|
553
720
|
async (name: string): Promise<HyperliquidWallet> => {
|
|
554
|
-
const { response } = await tgClient.hyperliquidWalletCreate(
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
721
|
+
const { response } = await tgClient.hyperliquidWalletCreate(
|
|
722
|
+
{
|
|
723
|
+
authData: {},
|
|
724
|
+
name,
|
|
725
|
+
},
|
|
726
|
+
telegramRpcOptions,
|
|
727
|
+
);
|
|
558
728
|
refreshWallets();
|
|
559
729
|
if (!response.wallet)
|
|
560
730
|
throw new Error("Wallet creation returned no wallet");
|
|
561
731
|
return response.wallet;
|
|
562
732
|
},
|
|
563
|
-
[tgClient,
|
|
733
|
+
[tgClient, telegramRpcOptions, refreshWallets],
|
|
564
734
|
);
|
|
565
735
|
|
|
566
736
|
const deleteWallet = useCallback(
|
|
567
737
|
async (walletId: number): Promise<void> => {
|
|
568
|
-
await tgClient.hyperliquidWalletDelete(
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
738
|
+
await tgClient.hyperliquidWalletDelete(
|
|
739
|
+
{
|
|
740
|
+
authData: {},
|
|
741
|
+
walletId,
|
|
742
|
+
},
|
|
743
|
+
telegramRpcOptions,
|
|
744
|
+
);
|
|
572
745
|
if (walletId === selectedWalletId) {
|
|
573
746
|
const remaining = wallets.filter((w) => w.id !== walletId);
|
|
574
747
|
setSelectedWalletId(remaining[0]?.id ?? 0);
|
|
575
748
|
}
|
|
576
749
|
refreshWallets();
|
|
577
750
|
},
|
|
578
|
-
[tgClient,
|
|
751
|
+
[tgClient, telegramRpcOptions, selectedWalletId, wallets, refreshWallets],
|
|
579
752
|
);
|
|
580
753
|
|
|
581
754
|
const createWalletPack = useCallback(
|
|
582
755
|
async (name: string): Promise<number> => {
|
|
583
|
-
const { response } = await tgClient.telegramChatWalletPackCreate(
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
756
|
+
const { response } = await tgClient.telegramChatWalletPackCreate(
|
|
757
|
+
{
|
|
758
|
+
authData: {},
|
|
759
|
+
name,
|
|
760
|
+
},
|
|
761
|
+
telegramRpcOptions,
|
|
762
|
+
);
|
|
587
763
|
refreshWallets();
|
|
588
764
|
return response.packId;
|
|
589
765
|
},
|
|
590
|
-
[tgClient,
|
|
766
|
+
[tgClient, telegramRpcOptions, refreshWallets],
|
|
591
767
|
);
|
|
592
768
|
|
|
593
769
|
const addPackLabel = useCallback(
|
|
@@ -596,13 +772,16 @@ export function HypurrConnectProvider({
|
|
|
596
772
|
walletLabel: string;
|
|
597
773
|
packId: number;
|
|
598
774
|
}): Promise<void> => {
|
|
599
|
-
await tgClient.telegramChatWalletPackLabelAdd(
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
775
|
+
await tgClient.telegramChatWalletPackLabelAdd(
|
|
776
|
+
{
|
|
777
|
+
authData: {},
|
|
778
|
+
...params,
|
|
779
|
+
},
|
|
780
|
+
telegramRpcOptions,
|
|
781
|
+
);
|
|
603
782
|
refreshWallets();
|
|
604
783
|
},
|
|
605
|
-
[tgClient,
|
|
784
|
+
[tgClient, telegramRpcOptions, refreshWallets],
|
|
606
785
|
);
|
|
607
786
|
|
|
608
787
|
const modifyPackLabel = useCallback(
|
|
@@ -611,24 +790,30 @@ export function HypurrConnectProvider({
|
|
|
611
790
|
walletLabelNew: string;
|
|
612
791
|
packId: number;
|
|
613
792
|
}): Promise<void> => {
|
|
614
|
-
await tgClient.telegramChatWalletPackLabelModify(
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
793
|
+
await tgClient.telegramChatWalletPackLabelModify(
|
|
794
|
+
{
|
|
795
|
+
authData: {},
|
|
796
|
+
...params,
|
|
797
|
+
},
|
|
798
|
+
telegramRpcOptions,
|
|
799
|
+
);
|
|
618
800
|
refreshWallets();
|
|
619
801
|
},
|
|
620
|
-
[tgClient,
|
|
802
|
+
[tgClient, telegramRpcOptions, refreshWallets],
|
|
621
803
|
);
|
|
622
804
|
|
|
623
805
|
const removePackLabel = useCallback(
|
|
624
806
|
async (params: { walletLabel: string; packId: number }): Promise<void> => {
|
|
625
|
-
await tgClient.telegramChatWalletPackLabelRemove(
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
807
|
+
await tgClient.telegramChatWalletPackLabelRemove(
|
|
808
|
+
{
|
|
809
|
+
authData: {},
|
|
810
|
+
...params,
|
|
811
|
+
},
|
|
812
|
+
telegramRpcOptions,
|
|
813
|
+
);
|
|
629
814
|
refreshWallets();
|
|
630
815
|
},
|
|
631
|
-
[tgClient,
|
|
816
|
+
[tgClient, telegramRpcOptions, refreshWallets],
|
|
632
817
|
);
|
|
633
818
|
|
|
634
819
|
// ── TWAP session management (Telegram only) ─────────────────
|
|
@@ -639,11 +824,14 @@ export function HypurrConnectProvider({
|
|
|
639
824
|
"authData" | "walletId"
|
|
640
825
|
>,
|
|
641
826
|
) => {
|
|
642
|
-
const { response } = await tgClient.hyperliquidTwapCreate(
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
827
|
+
const { response } = await tgClient.hyperliquidTwapCreate(
|
|
828
|
+
{
|
|
829
|
+
authData: {},
|
|
830
|
+
walletId: selectedWalletId,
|
|
831
|
+
...params,
|
|
832
|
+
},
|
|
833
|
+
telegramRpcOptions,
|
|
834
|
+
);
|
|
647
835
|
if (!response.session)
|
|
648
836
|
throw new Error("TWAP creation returned no session");
|
|
649
837
|
const session = response.session;
|
|
@@ -656,7 +844,7 @@ export function HypurrConnectProvider({
|
|
|
656
844
|
);
|
|
657
845
|
return session;
|
|
658
846
|
},
|
|
659
|
-
[tgClient,
|
|
847
|
+
[tgClient, telegramRpcOptions, selectedWalletId],
|
|
660
848
|
);
|
|
661
849
|
|
|
662
850
|
const modifyTwap = useCallback(
|
|
@@ -666,13 +854,15 @@ export function HypurrConnectProvider({
|
|
|
666
854
|
"authData" | "walletId"
|
|
667
855
|
>,
|
|
668
856
|
) => {
|
|
669
|
-
const { response } = await tgClient.hyperliquidTwapModify(
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
857
|
+
const { response } = await tgClient.hyperliquidTwapModify(
|
|
858
|
+
{
|
|
859
|
+
authData: {},
|
|
860
|
+
walletId: selectedWalletId,
|
|
861
|
+
...params,
|
|
862
|
+
},
|
|
863
|
+
telegramRpcOptions,
|
|
864
|
+
);
|
|
865
|
+
if (!response.session) throw new Error("TWAP modify returned no session");
|
|
676
866
|
const session = response.session;
|
|
677
867
|
setWallets((prev) =>
|
|
678
868
|
prev.map((w) =>
|
|
@@ -688,16 +878,19 @@ export function HypurrConnectProvider({
|
|
|
688
878
|
);
|
|
689
879
|
return session;
|
|
690
880
|
},
|
|
691
|
-
[tgClient,
|
|
881
|
+
[tgClient, telegramRpcOptions, selectedWalletId],
|
|
692
882
|
);
|
|
693
883
|
|
|
694
884
|
const cancelTwap = useCallback(
|
|
695
885
|
async (sessionId: number) => {
|
|
696
|
-
await tgClient.hyperliquidTwapCancel(
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
886
|
+
await tgClient.hyperliquidTwapCancel(
|
|
887
|
+
{
|
|
888
|
+
authData: {},
|
|
889
|
+
walletId: selectedWalletId,
|
|
890
|
+
twapSessionId: sessionId,
|
|
891
|
+
},
|
|
892
|
+
telegramRpcOptions,
|
|
893
|
+
);
|
|
701
894
|
setWallets((prev) =>
|
|
702
895
|
prev.map((w) =>
|
|
703
896
|
w.id === selectedWalletId
|
|
@@ -709,7 +902,7 @@ export function HypurrConnectProvider({
|
|
|
709
902
|
),
|
|
710
903
|
);
|
|
711
904
|
},
|
|
712
|
-
[tgClient,
|
|
905
|
+
[tgClient, telegramRpcOptions, selectedWalletId],
|
|
713
906
|
);
|
|
714
907
|
|
|
715
908
|
// ── Scale session management (Telegram only) ───────────────
|
|
@@ -720,11 +913,14 @@ export function HypurrConnectProvider({
|
|
|
720
913
|
"authData" | "walletId"
|
|
721
914
|
>,
|
|
722
915
|
) => {
|
|
723
|
-
const { response } = await tgClient.hyperliquidScaleCreate(
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
916
|
+
const { response } = await tgClient.hyperliquidScaleCreate(
|
|
917
|
+
{
|
|
918
|
+
authData: {},
|
|
919
|
+
walletId: selectedWalletId,
|
|
920
|
+
...params,
|
|
921
|
+
},
|
|
922
|
+
telegramRpcOptions,
|
|
923
|
+
);
|
|
728
924
|
if (!response.session)
|
|
729
925
|
throw new Error("Scale creation returned no session");
|
|
730
926
|
const session = response.session;
|
|
@@ -737,16 +933,19 @@ export function HypurrConnectProvider({
|
|
|
737
933
|
);
|
|
738
934
|
return session;
|
|
739
935
|
},
|
|
740
|
-
[tgClient,
|
|
936
|
+
[tgClient, telegramRpcOptions, selectedWalletId],
|
|
741
937
|
);
|
|
742
938
|
|
|
743
939
|
const cancelScale = useCallback(
|
|
744
940
|
async (sessionId: number) => {
|
|
745
|
-
await tgClient.hyperliquidScaleCancel(
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
941
|
+
await tgClient.hyperliquidScaleCancel(
|
|
942
|
+
{
|
|
943
|
+
authData: {},
|
|
944
|
+
walletId: selectedWalletId,
|
|
945
|
+
scaleSessionId: sessionId,
|
|
946
|
+
},
|
|
947
|
+
telegramRpcOptions,
|
|
948
|
+
);
|
|
750
949
|
setWallets((prev) =>
|
|
751
950
|
prev.map((w) =>
|
|
752
951
|
w.id === selectedWalletId
|
|
@@ -760,7 +959,7 @@ export function HypurrConnectProvider({
|
|
|
760
959
|
),
|
|
761
960
|
);
|
|
762
961
|
},
|
|
763
|
-
[tgClient,
|
|
962
|
+
[tgClient, telegramRpcOptions, selectedWalletId],
|
|
764
963
|
);
|
|
765
964
|
|
|
766
965
|
// ── Login modal state ────────────────────────────────────────
|
|
@@ -769,23 +968,60 @@ export function HypurrConnectProvider({
|
|
|
769
968
|
const closeLoginModal = useCallback(() => setLoginModalOpen(false), []);
|
|
770
969
|
|
|
771
970
|
// ── Auth actions ─────────────────────────────────────────────
|
|
772
|
-
const loginTelegram = useCallback((
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
971
|
+
const loginTelegram = useCallback(() => {
|
|
972
|
+
const state = randomState();
|
|
973
|
+
sessionStorage.setItem(TELEGRAM_AUTH_STATE_KEY, state);
|
|
974
|
+
|
|
975
|
+
const configuredReturnTo = config.telegram.returnTo;
|
|
976
|
+
const returnTo =
|
|
977
|
+
typeof configuredReturnTo === "function"
|
|
978
|
+
? configuredReturnTo()
|
|
979
|
+
: configuredReturnTo || currentReturnTo();
|
|
980
|
+
|
|
981
|
+
const authUrl = new URL(config.telegram.authHubUrl || DEFAULT_AUTH_HUB_URL);
|
|
982
|
+
authUrl.searchParams.set("return_to", returnTo);
|
|
983
|
+
authUrl.searchParams.set("state", state);
|
|
984
|
+
authUrl.searchParams.set("scope", normalizeScopes(config.telegram.scope));
|
|
985
|
+
|
|
986
|
+
const width = 520;
|
|
987
|
+
const height = 720;
|
|
988
|
+
const left = window.screenX + Math.max(0, (window.outerWidth - width) / 2);
|
|
989
|
+
const top = window.screenY + Math.max(0, (window.outerHeight - height) / 2);
|
|
990
|
+
const popup = window.open(
|
|
991
|
+
authUrl.toString(),
|
|
992
|
+
"hypurr_telegram_auth",
|
|
993
|
+
[
|
|
994
|
+
`width=${width}`,
|
|
995
|
+
`height=${height}`,
|
|
996
|
+
`left=${Math.round(left)}`,
|
|
997
|
+
`top=${Math.round(top)}`,
|
|
998
|
+
"resizable=yes",
|
|
999
|
+
"scrollbars=yes",
|
|
1000
|
+
].join(","),
|
|
1001
|
+
);
|
|
1002
|
+
|
|
1003
|
+
if (popup) {
|
|
1004
|
+
popup.focus();
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
window.location.assign(authUrl.toString());
|
|
1009
|
+
}, [
|
|
1010
|
+
config.telegram.authHubUrl,
|
|
1011
|
+
config.telegram.returnTo,
|
|
1012
|
+
config.telegram.scope,
|
|
1013
|
+
]);
|
|
779
1014
|
|
|
780
1015
|
const connectEoa = useCallback(
|
|
781
1016
|
(address: `0x${string}`, signer?: EoaSigner) => {
|
|
782
1017
|
eoaSignerRef.current = signer ?? null;
|
|
783
1018
|
setEoaAddress(address);
|
|
784
|
-
|
|
1019
|
+
setTgAuthToken(null);
|
|
785
1020
|
setTgUser(null);
|
|
786
1021
|
setTgError(null);
|
|
787
1022
|
setEoaError(null);
|
|
788
1023
|
localStorage.removeItem(TELEGRAM_STORAGE_KEY);
|
|
1024
|
+
localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
|
|
789
1025
|
|
|
790
1026
|
const existing = loadAgent(address);
|
|
791
1027
|
if (existing && existing.validUntil > Date.now()) {
|
|
@@ -903,14 +1139,15 @@ export function HypurrConnectProvider({
|
|
|
903
1139
|
);
|
|
904
1140
|
|
|
905
1141
|
const logout = useCallback(() => {
|
|
906
|
-
setTgLoginData(null);
|
|
907
1142
|
setTgUser(null);
|
|
908
1143
|
setTgError(null);
|
|
1144
|
+
setTgAuthToken(null);
|
|
909
1145
|
setEoaAddress(null);
|
|
910
1146
|
setAgent(null);
|
|
911
1147
|
setEoaError(null);
|
|
912
1148
|
eoaSignerRef.current = null;
|
|
913
1149
|
localStorage.removeItem(TELEGRAM_STORAGE_KEY);
|
|
1150
|
+
localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
|
|
914
1151
|
}, []);
|
|
915
1152
|
|
|
916
1153
|
// ── Context value ────────────────────────────────────────────
|
|
@@ -962,6 +1199,8 @@ export function HypurrConnectProvider({
|
|
|
962
1199
|
useWidget: config.telegram?.useWidget ?? false,
|
|
963
1200
|
|
|
964
1201
|
authDataMap,
|
|
1202
|
+
authToken: tgAuthToken,
|
|
1203
|
+
telegramRpcOptions,
|
|
965
1204
|
telegramClient: tgClient,
|
|
966
1205
|
staticClient,
|
|
967
1206
|
}),
|
|
@@ -1003,6 +1242,8 @@ export function HypurrConnectProvider({
|
|
|
1003
1242
|
config.telegram?.botUsername,
|
|
1004
1243
|
config.telegram?.useWidget,
|
|
1005
1244
|
authDataMap,
|
|
1245
|
+
tgAuthToken,
|
|
1246
|
+
telegramRpcOptions,
|
|
1006
1247
|
tgClient,
|
|
1007
1248
|
staticClient,
|
|
1008
1249
|
],
|