@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.
@@ -3,7 +3,11 @@ import {
3
3
  HttpTransport,
4
4
  type IRequestTransport,
5
5
  } from "@hfunlabs/hyperliquid";
6
- import { PrivateKeySigner, signUserSignedAction } from "@hfunlabs/hyperliquid/signing";
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: (data: TelegramLoginData) => void;
51
+ loginTelegram: () => void;
49
52
  botUsername: string;
50
53
  useWidget: boolean;
51
54
  }
52
55
 
53
- const TELEGRAM_STORAGE_KEY = "hypurr-connect-tg-user";
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 toAuthDataMap(data: TelegramLoginData): Record<string, string> {
56
- const map: Record<string, string> = {
57
- id: String(data.id),
58
- first_name: data.first_name,
59
- auth_date: String(data.auth_date),
60
- hash: data.hash,
61
- };
62
- if (data.last_name) map.last_name = data.last_name;
63
- if (data.username) map.username = data.username;
64
- if (data.photo_url) map.photo_url = data.photo_url;
65
- return map;
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 [tgLoginData, setTgLoginData] = useState<TelegramLoginData | null>(
101
- () => {
102
- try {
103
- const stored = localStorage.getItem(TELEGRAM_STORAGE_KEY);
104
- return stored ? JSON.parse(stored) : null;
105
- } catch {
106
- return null;
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
- () => (tgLoginData ? toAuthDataMap(tgLoginData) : {}),
116
- [tgLoginData],
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
- if (!tgLoginData) return;
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
- }, [tgLoginData, tgClient, tgUserTick]);
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 = tgLoginData
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
- authData: authDataMap,
225
- walletId: selectedWalletId,
226
- }),
227
- tgClient.hyperliquidWalletScaleSessions({
228
- authData: authDataMap,
229
- walletId: selectedWalletId,
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
- }, [authMethod, selectedWalletId, pollInterval, tgClient, authDataMap]);
416
+ }, [
417
+ authMethod,
418
+ selectedWalletId,
419
+ pollInterval,
420
+ tgClient,
421
+ telegramRpcOptions,
422
+ ]);
257
423
 
258
424
  const user = useMemo<HypurrUser | null>(() => {
259
- if (tgLoginData && authMethod === "telegram" && selectedWallet) {
425
+ if (tgAuthToken && authMethod === "telegram" && selectedWallet && tgUser) {
260
426
  return {
261
427
  address: selectedWallet.ethereumAddress,
262
428
  walletId: selectedWallet.id,
263
- displayName: tgLoginData.username
264
- ? `@${tgLoginData.username}`
265
- : tgLoginData.first_name,
266
- photoUrl: tgLoginData.photo_url,
429
+ displayName: tgUser.telegramUsername
430
+ ? `@${tgUser.telegramUsername}`
431
+ : `Telegram ${tgUser.telegramId}`,
267
432
  authMethod: "telegram",
268
- telegramId: String(tgLoginData.id),
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
- }, [tgLoginData, selectedWallet, eoaAddress, authMethod, tgUser]);
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
- authDataMap,
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 = `0x${signer.chainId.toString(16)}` as `0x${string}`;
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
- authDataMap,
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
- authData: authDataMap,
556
- name,
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, authDataMap, refreshWallets],
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
- authData: authDataMap,
570
- walletId,
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, authDataMap, selectedWalletId, wallets, refreshWallets],
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
- authData: authDataMap,
585
- name,
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, authDataMap, refreshWallets],
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
- authData: authDataMap,
601
- ...params,
602
- });
775
+ await tgClient.telegramChatWalletPackLabelAdd(
776
+ {
777
+ authData: {},
778
+ ...params,
779
+ },
780
+ telegramRpcOptions,
781
+ );
603
782
  refreshWallets();
604
783
  },
605
- [tgClient, authDataMap, refreshWallets],
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
- authData: authDataMap,
616
- ...params,
617
- });
793
+ await tgClient.telegramChatWalletPackLabelModify(
794
+ {
795
+ authData: {},
796
+ ...params,
797
+ },
798
+ telegramRpcOptions,
799
+ );
618
800
  refreshWallets();
619
801
  },
620
- [tgClient, authDataMap, refreshWallets],
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
- authData: authDataMap,
627
- ...params,
628
- });
807
+ await tgClient.telegramChatWalletPackLabelRemove(
808
+ {
809
+ authData: {},
810
+ ...params,
811
+ },
812
+ telegramRpcOptions,
813
+ );
629
814
  refreshWallets();
630
815
  },
631
- [tgClient, authDataMap, refreshWallets],
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
- authData: authDataMap,
644
- walletId: selectedWalletId,
645
- ...params,
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, authDataMap, selectedWalletId],
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
- authData: authDataMap,
671
- walletId: selectedWalletId,
672
- ...params,
673
- });
674
- if (!response.session)
675
- throw new Error("TWAP modify returned no session");
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, authDataMap, selectedWalletId],
881
+ [tgClient, telegramRpcOptions, selectedWalletId],
692
882
  );
693
883
 
694
884
  const cancelTwap = useCallback(
695
885
  async (sessionId: number) => {
696
- await tgClient.hyperliquidTwapCancel({
697
- authData: authDataMap,
698
- walletId: selectedWalletId,
699
- twapSessionId: sessionId,
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, authDataMap, selectedWalletId],
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
- authData: authDataMap,
725
- walletId: selectedWalletId,
726
- ...params,
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, authDataMap, selectedWalletId],
936
+ [tgClient, telegramRpcOptions, selectedWalletId],
741
937
  );
742
938
 
743
939
  const cancelScale = useCallback(
744
940
  async (sessionId: number) => {
745
- await tgClient.hyperliquidScaleCancel({
746
- authData: authDataMap,
747
- walletId: selectedWalletId,
748
- scaleSessionId: sessionId,
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, authDataMap, selectedWalletId],
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((data: TelegramLoginData) => {
773
- setTgLoginData(data);
774
- localStorage.setItem(TELEGRAM_STORAGE_KEY, JSON.stringify(data));
775
- setEoaAddress(null);
776
- setAgent(null);
777
- setEoaError(null);
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
- setTgLoginData(null);
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
  ],