@hfunlabs/hypurr-connect 0.1.9 → 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,30 +44,30 @@ 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";
54
-
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;
66
- }
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
+ ];
67
71
 
68
72
  function isInvalidTelegramAuthError(err: unknown): boolean {
69
73
  const msg =
@@ -72,7 +76,55 @@ function isInvalidTelegramAuthError(err: unknown): boolean {
72
76
  : typeof err === "object" && err !== null && "message" in err
73
77
  ? String((err as { message: unknown }).message)
74
78
  : String(err);
75
- return /invalid telegram auth data/i.test(msg);
79
+ return /invalid telegram auth data|invalid auth token|missing authorization token/i.test(
80
+ msg,
81
+ );
82
+ }
83
+
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
+ );
76
128
  }
77
129
 
78
130
  const HypurrConnectContext = createContext<InternalConnectState | null>(null);
@@ -107,23 +159,24 @@ export function HypurrConnectProvider({
107
159
  const staticClient = useMemo(() => createStaticClient(config), [config]);
108
160
 
109
161
  // ── Telegram auth state ──────────────────────────────────────
110
- const [tgLoginData, setTgLoginData] = useState<TelegramLoginData | null>(
111
- () => {
112
- try {
113
- const stored = localStorage.getItem(TELEGRAM_STORAGE_KEY);
114
- return stored ? JSON.parse(stored) : null;
115
- } catch {
116
- return null;
117
- }
118
- },
119
- );
162
+ const [tgAuthToken, setTgAuthToken] = useState<string | null>(() => {
163
+ try {
164
+ return localStorage.getItem(TELEGRAM_STORAGE_KEY);
165
+ } catch {
166
+ return null;
167
+ }
168
+ });
120
169
  const [tgUser, setTgUser] = useState<HypurrTelegramUser | null>(null);
121
170
  const [tgLoading, setTgLoading] = useState(false);
122
171
  const [tgError, setTgError] = useState<string | null>(null);
123
172
 
124
- const authDataMap = useMemo(
125
- () => (tgLoginData ? toAuthDataMap(tgLoginData) : {}),
126
- [tgLoginData],
173
+ const authDataMap = useMemo(() => ({}), []);
174
+ const telegramRpcOptions = useMemo<RpcOptions | undefined>(
175
+ () =>
176
+ tgAuthToken
177
+ ? { meta: { authorization: `Bearer ${tgAuthToken}` } }
178
+ : undefined,
179
+ [tgAuthToken],
127
180
  );
128
181
 
129
182
  const [tgUserTick, setTgUserTick] = useState(0);
@@ -131,26 +184,99 @@ export function HypurrConnectProvider({
131
184
  // Auto-disconnect when the server rejects telegram auth data.
132
185
  const onInvalidAuthRef = useRef<(() => void) | null>(null);
133
186
  onInvalidAuthRef.current = () => {
134
- console.warn("[HypurrConnect] Invalid telegram auth data — disconnecting.");
135
- setTgLoginData(null);
187
+ console.warn(
188
+ "[HypurrConnect] Invalid Telegram auth token — disconnecting.",
189
+ );
190
+ setTgAuthToken(null);
136
191
  setTgUser(null);
137
192
  setTgError(null);
138
193
  localStorage.removeItem(TELEGRAM_STORAGE_KEY);
194
+ localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
139
195
  };
140
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
+
141
204
  useEffect(() => {
142
- 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;
143
270
  let cancelled = false;
144
271
  setTgLoading(true);
145
272
  setTgError(null);
146
273
 
147
274
  (async () => {
148
275
  try {
149
- const authData = toAuthDataMap(tgLoginData);
150
276
  const [{ response: userResp }, { response: walletsResp }] =
151
277
  await Promise.all([
152
- tgClient.telegramUser({ authData }),
153
- tgClient.telegramUserWallets({ authData }),
278
+ tgClient.telegramUser({ authData: {} }, telegramRpcOptions),
279
+ tgClient.telegramUserWallets({ authData: {} }, telegramRpcOptions),
154
280
  ]);
155
281
  if (cancelled) return;
156
282
  const user = (userResp as TelegramUserResponse).user ?? null;
@@ -176,7 +302,7 @@ export function HypurrConnectProvider({
176
302
  return () => {
177
303
  cancelled = true;
178
304
  };
179
- }, [tgLoginData, tgClient, tgUserTick]);
305
+ }, [tgAuthToken, telegramRpcOptions, tgClient, tgUserTick]);
180
306
 
181
307
  // ── EOA auth state ───────────────────────────────────────────
182
308
  const [eoaAddress, setEoaAddress] = useState<`0x${string}` | null>(null);
@@ -186,7 +312,7 @@ export function HypurrConnectProvider({
186
312
  const eoaSignerRef = useRef<EoaSigner | null>(null);
187
313
 
188
314
  // ── Derived auth ─────────────────────────────────────────────
189
- const authMethod: AuthMethod = tgLoginData
315
+ const authMethod: AuthMethod = tgAuthToken
190
316
  ? "telegram"
191
317
  : eoaAddress
192
318
  ? "eoa"
@@ -244,14 +370,20 @@ export function HypurrConnectProvider({
244
370
  try {
245
371
  const [{ response: twapResp }, { response: scaleResp }] =
246
372
  await Promise.all([
247
- tgClient.hyperliquidWalletTwapSessions({
248
- authData: authDataMap,
249
- walletId: selectedWalletId,
250
- }),
251
- tgClient.hyperliquidWalletScaleSessions({
252
- authData: authDataMap,
253
- walletId: selectedWalletId,
254
- }),
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
+ ),
255
387
  ]);
256
388
  if (cancelled) return;
257
389
  setWallets((prev) =>
@@ -281,19 +413,24 @@ export function HypurrConnectProvider({
281
413
  cancelled = true;
282
414
  clearInterval(id);
283
415
  };
284
- }, [authMethod, selectedWalletId, pollInterval, tgClient, authDataMap]);
416
+ }, [
417
+ authMethod,
418
+ selectedWalletId,
419
+ pollInterval,
420
+ tgClient,
421
+ telegramRpcOptions,
422
+ ]);
285
423
 
286
424
  const user = useMemo<HypurrUser | null>(() => {
287
- if (tgLoginData && authMethod === "telegram" && selectedWallet) {
425
+ if (tgAuthToken && authMethod === "telegram" && selectedWallet && tgUser) {
288
426
  return {
289
427
  address: selectedWallet.ethereumAddress,
290
428
  walletId: selectedWallet.id,
291
- displayName: tgLoginData.username
292
- ? `@${tgLoginData.username}`
293
- : tgLoginData.first_name,
294
- photoUrl: tgLoginData.photo_url,
429
+ displayName: tgUser.telegramUsername
430
+ ? `@${tgUser.telegramUsername}`
431
+ : `Telegram ${tgUser.telegramId}`,
295
432
  authMethod: "telegram",
296
- telegramId: String(tgLoginData.id),
433
+ telegramId: String(tgUser.telegramId),
297
434
  hfunScore: tgUser?.reputation?.hfunScore,
298
435
  reputationScore: tgUser?.reputation?.reputationScore,
299
436
  };
@@ -307,7 +444,7 @@ export function HypurrConnectProvider({
307
444
  };
308
445
  }
309
446
  return null;
310
- }, [tgLoginData, selectedWallet, eoaAddress, authMethod, tgUser]);
447
+ }, [tgAuthToken, selectedWallet, eoaAddress, authMethod, tgUser]);
311
448
 
312
449
  // ── Exchange client ──────────────────────────────────────────
313
450
  // Telegram: GrpcExchangeTransport → HyperliquidCoreAction (server signs)
@@ -350,7 +487,7 @@ export function HypurrConnectProvider({
350
487
  const transport = new GrpcExchangeTransport({
351
488
  isTestnet: config.isTestnet ?? false,
352
489
  telegramClient: tgClient,
353
- authDataMap,
490
+ rpcOptions: telegramRpcOptions,
354
491
  walletId: user.walletId,
355
492
  onAuthError: () => onInvalidAuthRef.current?.(),
356
493
  });
@@ -435,7 +572,8 @@ export function HypurrConnectProvider({
435
572
  const { privateKey, address: agentAddress } =
436
573
  await generateAgentKey();
437
574
 
438
- const chainIdHex = `0x${signer.chainId.toString(16)}` as `0x${string}`;
575
+ const chainIdHex =
576
+ `0x${signer.chainId.toString(16)}` as `0x${string}`;
439
577
  const nonce = Date.now();
440
578
  const action = {
441
579
  type: "approveAgent" as const,
@@ -567,7 +705,7 @@ export function HypurrConnectProvider({
567
705
  eoaAddress,
568
706
  config.isTestnet,
569
707
  tgClient,
570
- authDataMap,
708
+ telegramRpcOptions,
571
709
  ]);
572
710
 
573
711
  const handleClearAgent = useCallback(() => {
@@ -580,43 +718,52 @@ export function HypurrConnectProvider({
580
718
  // ── Wallet management (Telegram only) ───────────────────────
581
719
  const createWallet = useCallback(
582
720
  async (name: string): Promise<HyperliquidWallet> => {
583
- const { response } = await tgClient.hyperliquidWalletCreate({
584
- authData: authDataMap,
585
- name,
586
- });
721
+ const { response } = await tgClient.hyperliquidWalletCreate(
722
+ {
723
+ authData: {},
724
+ name,
725
+ },
726
+ telegramRpcOptions,
727
+ );
587
728
  refreshWallets();
588
729
  if (!response.wallet)
589
730
  throw new Error("Wallet creation returned no wallet");
590
731
  return response.wallet;
591
732
  },
592
- [tgClient, authDataMap, refreshWallets],
733
+ [tgClient, telegramRpcOptions, refreshWallets],
593
734
  );
594
735
 
595
736
  const deleteWallet = useCallback(
596
737
  async (walletId: number): Promise<void> => {
597
- await tgClient.hyperliquidWalletDelete({
598
- authData: authDataMap,
599
- walletId,
600
- });
738
+ await tgClient.hyperliquidWalletDelete(
739
+ {
740
+ authData: {},
741
+ walletId,
742
+ },
743
+ telegramRpcOptions,
744
+ );
601
745
  if (walletId === selectedWalletId) {
602
746
  const remaining = wallets.filter((w) => w.id !== walletId);
603
747
  setSelectedWalletId(remaining[0]?.id ?? 0);
604
748
  }
605
749
  refreshWallets();
606
750
  },
607
- [tgClient, authDataMap, selectedWalletId, wallets, refreshWallets],
751
+ [tgClient, telegramRpcOptions, selectedWalletId, wallets, refreshWallets],
608
752
  );
609
753
 
610
754
  const createWalletPack = useCallback(
611
755
  async (name: string): Promise<number> => {
612
- const { response } = await tgClient.telegramChatWalletPackCreate({
613
- authData: authDataMap,
614
- name,
615
- });
756
+ const { response } = await tgClient.telegramChatWalletPackCreate(
757
+ {
758
+ authData: {},
759
+ name,
760
+ },
761
+ telegramRpcOptions,
762
+ );
616
763
  refreshWallets();
617
764
  return response.packId;
618
765
  },
619
- [tgClient, authDataMap, refreshWallets],
766
+ [tgClient, telegramRpcOptions, refreshWallets],
620
767
  );
621
768
 
622
769
  const addPackLabel = useCallback(
@@ -625,13 +772,16 @@ export function HypurrConnectProvider({
625
772
  walletLabel: string;
626
773
  packId: number;
627
774
  }): Promise<void> => {
628
- await tgClient.telegramChatWalletPackLabelAdd({
629
- authData: authDataMap,
630
- ...params,
631
- });
775
+ await tgClient.telegramChatWalletPackLabelAdd(
776
+ {
777
+ authData: {},
778
+ ...params,
779
+ },
780
+ telegramRpcOptions,
781
+ );
632
782
  refreshWallets();
633
783
  },
634
- [tgClient, authDataMap, refreshWallets],
784
+ [tgClient, telegramRpcOptions, refreshWallets],
635
785
  );
636
786
 
637
787
  const modifyPackLabel = useCallback(
@@ -640,24 +790,30 @@ export function HypurrConnectProvider({
640
790
  walletLabelNew: string;
641
791
  packId: number;
642
792
  }): Promise<void> => {
643
- await tgClient.telegramChatWalletPackLabelModify({
644
- authData: authDataMap,
645
- ...params,
646
- });
793
+ await tgClient.telegramChatWalletPackLabelModify(
794
+ {
795
+ authData: {},
796
+ ...params,
797
+ },
798
+ telegramRpcOptions,
799
+ );
647
800
  refreshWallets();
648
801
  },
649
- [tgClient, authDataMap, refreshWallets],
802
+ [tgClient, telegramRpcOptions, refreshWallets],
650
803
  );
651
804
 
652
805
  const removePackLabel = useCallback(
653
806
  async (params: { walletLabel: string; packId: number }): Promise<void> => {
654
- await tgClient.telegramChatWalletPackLabelRemove({
655
- authData: authDataMap,
656
- ...params,
657
- });
807
+ await tgClient.telegramChatWalletPackLabelRemove(
808
+ {
809
+ authData: {},
810
+ ...params,
811
+ },
812
+ telegramRpcOptions,
813
+ );
658
814
  refreshWallets();
659
815
  },
660
- [tgClient, authDataMap, refreshWallets],
816
+ [tgClient, telegramRpcOptions, refreshWallets],
661
817
  );
662
818
 
663
819
  // ── TWAP session management (Telegram only) ─────────────────
@@ -668,11 +824,14 @@ export function HypurrConnectProvider({
668
824
  "authData" | "walletId"
669
825
  >,
670
826
  ) => {
671
- const { response } = await tgClient.hyperliquidTwapCreate({
672
- authData: authDataMap,
673
- walletId: selectedWalletId,
674
- ...params,
675
- });
827
+ const { response } = await tgClient.hyperliquidTwapCreate(
828
+ {
829
+ authData: {},
830
+ walletId: selectedWalletId,
831
+ ...params,
832
+ },
833
+ telegramRpcOptions,
834
+ );
676
835
  if (!response.session)
677
836
  throw new Error("TWAP creation returned no session");
678
837
  const session = response.session;
@@ -685,7 +844,7 @@ export function HypurrConnectProvider({
685
844
  );
686
845
  return session;
687
846
  },
688
- [tgClient, authDataMap, selectedWalletId],
847
+ [tgClient, telegramRpcOptions, selectedWalletId],
689
848
  );
690
849
 
691
850
  const modifyTwap = useCallback(
@@ -695,13 +854,15 @@ export function HypurrConnectProvider({
695
854
  "authData" | "walletId"
696
855
  >,
697
856
  ) => {
698
- const { response } = await tgClient.hyperliquidTwapModify({
699
- authData: authDataMap,
700
- walletId: selectedWalletId,
701
- ...params,
702
- });
703
- if (!response.session)
704
- 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");
705
866
  const session = response.session;
706
867
  setWallets((prev) =>
707
868
  prev.map((w) =>
@@ -717,16 +878,19 @@ export function HypurrConnectProvider({
717
878
  );
718
879
  return session;
719
880
  },
720
- [tgClient, authDataMap, selectedWalletId],
881
+ [tgClient, telegramRpcOptions, selectedWalletId],
721
882
  );
722
883
 
723
884
  const cancelTwap = useCallback(
724
885
  async (sessionId: number) => {
725
- await tgClient.hyperliquidTwapCancel({
726
- authData: authDataMap,
727
- walletId: selectedWalletId,
728
- twapSessionId: sessionId,
729
- });
886
+ await tgClient.hyperliquidTwapCancel(
887
+ {
888
+ authData: {},
889
+ walletId: selectedWalletId,
890
+ twapSessionId: sessionId,
891
+ },
892
+ telegramRpcOptions,
893
+ );
730
894
  setWallets((prev) =>
731
895
  prev.map((w) =>
732
896
  w.id === selectedWalletId
@@ -738,7 +902,7 @@ export function HypurrConnectProvider({
738
902
  ),
739
903
  );
740
904
  },
741
- [tgClient, authDataMap, selectedWalletId],
905
+ [tgClient, telegramRpcOptions, selectedWalletId],
742
906
  );
743
907
 
744
908
  // ── Scale session management (Telegram only) ───────────────
@@ -749,11 +913,14 @@ export function HypurrConnectProvider({
749
913
  "authData" | "walletId"
750
914
  >,
751
915
  ) => {
752
- const { response } = await tgClient.hyperliquidScaleCreate({
753
- authData: authDataMap,
754
- walletId: selectedWalletId,
755
- ...params,
756
- });
916
+ const { response } = await tgClient.hyperliquidScaleCreate(
917
+ {
918
+ authData: {},
919
+ walletId: selectedWalletId,
920
+ ...params,
921
+ },
922
+ telegramRpcOptions,
923
+ );
757
924
  if (!response.session)
758
925
  throw new Error("Scale creation returned no session");
759
926
  const session = response.session;
@@ -766,16 +933,19 @@ export function HypurrConnectProvider({
766
933
  );
767
934
  return session;
768
935
  },
769
- [tgClient, authDataMap, selectedWalletId],
936
+ [tgClient, telegramRpcOptions, selectedWalletId],
770
937
  );
771
938
 
772
939
  const cancelScale = useCallback(
773
940
  async (sessionId: number) => {
774
- await tgClient.hyperliquidScaleCancel({
775
- authData: authDataMap,
776
- walletId: selectedWalletId,
777
- scaleSessionId: sessionId,
778
- });
941
+ await tgClient.hyperliquidScaleCancel(
942
+ {
943
+ authData: {},
944
+ walletId: selectedWalletId,
945
+ scaleSessionId: sessionId,
946
+ },
947
+ telegramRpcOptions,
948
+ );
779
949
  setWallets((prev) =>
780
950
  prev.map((w) =>
781
951
  w.id === selectedWalletId
@@ -789,7 +959,7 @@ export function HypurrConnectProvider({
789
959
  ),
790
960
  );
791
961
  },
792
- [tgClient, authDataMap, selectedWalletId],
962
+ [tgClient, telegramRpcOptions, selectedWalletId],
793
963
  );
794
964
 
795
965
  // ── Login modal state ────────────────────────────────────────
@@ -798,23 +968,60 @@ export function HypurrConnectProvider({
798
968
  const closeLoginModal = useCallback(() => setLoginModalOpen(false), []);
799
969
 
800
970
  // ── Auth actions ─────────────────────────────────────────────
801
- const loginTelegram = useCallback((data: TelegramLoginData) => {
802
- setTgLoginData(data);
803
- localStorage.setItem(TELEGRAM_STORAGE_KEY, JSON.stringify(data));
804
- setEoaAddress(null);
805
- setAgent(null);
806
- setEoaError(null);
807
- }, []);
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
+ ]);
808
1014
 
809
1015
  const connectEoa = useCallback(
810
1016
  (address: `0x${string}`, signer?: EoaSigner) => {
811
1017
  eoaSignerRef.current = signer ?? null;
812
1018
  setEoaAddress(address);
813
- setTgLoginData(null);
1019
+ setTgAuthToken(null);
814
1020
  setTgUser(null);
815
1021
  setTgError(null);
816
1022
  setEoaError(null);
817
1023
  localStorage.removeItem(TELEGRAM_STORAGE_KEY);
1024
+ localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
818
1025
 
819
1026
  const existing = loadAgent(address);
820
1027
  if (existing && existing.validUntil > Date.now()) {
@@ -932,14 +1139,15 @@ export function HypurrConnectProvider({
932
1139
  );
933
1140
 
934
1141
  const logout = useCallback(() => {
935
- setTgLoginData(null);
936
1142
  setTgUser(null);
937
1143
  setTgError(null);
1144
+ setTgAuthToken(null);
938
1145
  setEoaAddress(null);
939
1146
  setAgent(null);
940
1147
  setEoaError(null);
941
1148
  eoaSignerRef.current = null;
942
1149
  localStorage.removeItem(TELEGRAM_STORAGE_KEY);
1150
+ localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
943
1151
  }, []);
944
1152
 
945
1153
  // ── Context value ────────────────────────────────────────────
@@ -991,6 +1199,8 @@ export function HypurrConnectProvider({
991
1199
  useWidget: config.telegram?.useWidget ?? false,
992
1200
 
993
1201
  authDataMap,
1202
+ authToken: tgAuthToken,
1203
+ telegramRpcOptions,
994
1204
  telegramClient: tgClient,
995
1205
  staticClient,
996
1206
  }),
@@ -1032,6 +1242,8 @@ export function HypurrConnectProvider({
1032
1242
  config.telegram?.botUsername,
1033
1243
  config.telegram?.useWidget,
1034
1244
  authDataMap,
1245
+ tgAuthToken,
1246
+ telegramRpcOptions,
1035
1247
  tgClient,
1036
1248
  staticClient,
1037
1249
  ],