@hfunlabs/hypurr-connect 0.1.9 → 0.1.11

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,31 @@ 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://auth.hypurr.fun/login";
61
+ const DEFAULT_MEDIA_URL = "https://media.hypurr.fun";
62
+ const DEFAULT_TELEGRAM_SCOPES = [
63
+ "telegram:user:read",
64
+ "telegram:wallet:read",
65
+ "telegram:wallet:write",
66
+ "telegram:trade:read",
67
+ "telegram:trade:write",
68
+ "telegram:cabal:read",
69
+ "telegram:cabal:write",
70
+ "telegram:agent:write",
71
+ ];
67
72
 
68
73
  function isInvalidTelegramAuthError(err: unknown): boolean {
69
74
  const msg =
@@ -72,7 +77,59 @@ function isInvalidTelegramAuthError(err: unknown): boolean {
72
77
  : typeof err === "object" && err !== null && "message" in err
73
78
  ? String((err as { message: unknown }).message)
74
79
  : String(err);
75
- return /invalid telegram auth data/i.test(msg);
80
+ return /invalid telegram auth data|invalid auth token|missing authorization token/i.test(
81
+ msg,
82
+ );
83
+ }
84
+
85
+ function normalizeMediaUrl(mediaUrl?: string): string {
86
+ return (mediaUrl?.trim() || DEFAULT_MEDIA_URL).replace(/\/+$/, "");
87
+ }
88
+
89
+ function currentReturnTo(): string {
90
+ const url = new URL(window.location.href);
91
+ for (const param of [
92
+ "token",
93
+ "token_type",
94
+ "token_source",
95
+ "state",
96
+ "scope",
97
+ ]) {
98
+ url.searchParams.delete(param);
99
+ }
100
+ return url.toString();
101
+ }
102
+
103
+ function randomState(): string {
104
+ const bytes = new Uint8Array(16);
105
+ crypto.getRandomValues(bytes);
106
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(
107
+ "",
108
+ );
109
+ }
110
+
111
+ function normalizeScopes(scope?: string | string[]): string {
112
+ if (Array.isArray(scope)) return scope.join(" ");
113
+ return scope?.trim() || DEFAULT_TELEGRAM_SCOPES.join(" ");
114
+ }
115
+
116
+ function isTelegramAuthMessage(
117
+ data: unknown,
118
+ ): data is {
119
+ type: typeof TELEGRAM_AUTH_MESSAGE;
120
+ token: string;
121
+ state: string;
122
+ } {
123
+ return (
124
+ typeof data === "object" &&
125
+ data !== null &&
126
+ "type" in data &&
127
+ "token" in data &&
128
+ "state" in data &&
129
+ (data as { type: unknown }).type === TELEGRAM_AUTH_MESSAGE &&
130
+ typeof (data as { token: unknown }).token === "string" &&
131
+ typeof (data as { state: unknown }).state === "string"
132
+ );
76
133
  }
77
134
 
78
135
  const HypurrConnectContext = createContext<InternalConnectState | null>(null);
@@ -107,23 +164,24 @@ export function HypurrConnectProvider({
107
164
  const staticClient = useMemo(() => createStaticClient(config), [config]);
108
165
 
109
166
  // ── 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
- );
167
+ const [tgAuthToken, setTgAuthToken] = useState<string | null>(() => {
168
+ try {
169
+ return localStorage.getItem(TELEGRAM_STORAGE_KEY);
170
+ } catch {
171
+ return null;
172
+ }
173
+ });
120
174
  const [tgUser, setTgUser] = useState<HypurrTelegramUser | null>(null);
121
175
  const [tgLoading, setTgLoading] = useState(false);
122
176
  const [tgError, setTgError] = useState<string | null>(null);
123
177
 
124
- const authDataMap = useMemo(
125
- () => (tgLoginData ? toAuthDataMap(tgLoginData) : {}),
126
- [tgLoginData],
178
+ const authDataMap = useMemo(() => ({}), []);
179
+ const telegramRpcOptions = useMemo<RpcOptions | undefined>(
180
+ () =>
181
+ tgAuthToken
182
+ ? { meta: { authorization: `Bearer ${tgAuthToken}` } }
183
+ : undefined,
184
+ [tgAuthToken],
127
185
  );
128
186
 
129
187
  const [tgUserTick, setTgUserTick] = useState(0);
@@ -131,26 +189,99 @@ export function HypurrConnectProvider({
131
189
  // Auto-disconnect when the server rejects telegram auth data.
132
190
  const onInvalidAuthRef = useRef<(() => void) | null>(null);
133
191
  onInvalidAuthRef.current = () => {
134
- console.warn("[HypurrConnect] Invalid telegram auth data — disconnecting.");
135
- setTgLoginData(null);
192
+ console.warn(
193
+ "[HypurrConnect] Invalid Telegram auth token — disconnecting.",
194
+ );
195
+ setTgAuthToken(null);
136
196
  setTgUser(null);
137
197
  setTgError(null);
138
198
  localStorage.removeItem(TELEGRAM_STORAGE_KEY);
199
+ localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
139
200
  };
140
201
 
202
+ const acceptTelegramToken = useCallback((token: string) => {
203
+ setTgAuthToken(token);
204
+ setTgError(null);
205
+ localStorage.setItem(TELEGRAM_STORAGE_KEY, token);
206
+ localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
207
+ }, []);
208
+
209
+ useEffect(() => {
210
+ const params = new URLSearchParams(window.location.search);
211
+ const token = params.get("token");
212
+ if (!token) {
213
+ localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
214
+ return;
215
+ }
216
+
217
+ const callbackState = params.get("state") ?? "";
218
+
219
+ if (window.opener && window.opener !== window) {
220
+ window.opener.postMessage(
221
+ {
222
+ type: TELEGRAM_AUTH_MESSAGE,
223
+ token,
224
+ state: callbackState,
225
+ },
226
+ window.location.origin,
227
+ );
228
+ window.close();
229
+ return;
230
+ }
231
+
232
+ const expectedState = sessionStorage.getItem(TELEGRAM_AUTH_STATE_KEY);
233
+ sessionStorage.removeItem(TELEGRAM_AUTH_STATE_KEY);
234
+ if (!expectedState || callbackState !== expectedState) {
235
+ setTgError("Invalid auth callback state.");
236
+ return;
237
+ }
238
+
239
+ acceptTelegramToken(token);
240
+
241
+ const cleanUrl = new URL(window.location.href);
242
+ for (const param of [
243
+ "token",
244
+ "token_type",
245
+ "token_source",
246
+ "state",
247
+ "scope",
248
+ ]) {
249
+ cleanUrl.searchParams.delete(param);
250
+ }
251
+ window.history.replaceState({}, document.title, cleanUrl.toString());
252
+ }, [acceptTelegramToken]);
253
+
141
254
  useEffect(() => {
142
- if (!tgLoginData) return;
255
+ function onMessage(event: MessageEvent) {
256
+ if (event.origin !== window.location.origin) return;
257
+ if (!isTelegramAuthMessage(event.data)) return;
258
+
259
+ const expectedState = sessionStorage.getItem(TELEGRAM_AUTH_STATE_KEY);
260
+ sessionStorage.removeItem(TELEGRAM_AUTH_STATE_KEY);
261
+ if (!expectedState || event.data.state !== expectedState) {
262
+ setTgError("Invalid auth callback state.");
263
+ return;
264
+ }
265
+
266
+ acceptTelegramToken(event.data.token);
267
+ }
268
+
269
+ window.addEventListener("message", onMessage);
270
+ return () => window.removeEventListener("message", onMessage);
271
+ }, [acceptTelegramToken]);
272
+
273
+ useEffect(() => {
274
+ if (!tgAuthToken || !telegramRpcOptions) return;
143
275
  let cancelled = false;
144
276
  setTgLoading(true);
145
277
  setTgError(null);
146
278
 
147
279
  (async () => {
148
280
  try {
149
- const authData = toAuthDataMap(tgLoginData);
150
281
  const [{ response: userResp }, { response: walletsResp }] =
151
282
  await Promise.all([
152
- tgClient.telegramUser({ authData }),
153
- tgClient.telegramUserWallets({ authData }),
283
+ tgClient.telegramUser({ authData: {} }, telegramRpcOptions),
284
+ tgClient.telegramUserWallets({ authData: {} }, telegramRpcOptions),
154
285
  ]);
155
286
  if (cancelled) return;
156
287
  const user = (userResp as TelegramUserResponse).user ?? null;
@@ -176,7 +307,7 @@ export function HypurrConnectProvider({
176
307
  return () => {
177
308
  cancelled = true;
178
309
  };
179
- }, [tgLoginData, tgClient, tgUserTick]);
310
+ }, [tgAuthToken, telegramRpcOptions, tgClient, tgUserTick]);
180
311
 
181
312
  // ── EOA auth state ───────────────────────────────────────────
182
313
  const [eoaAddress, setEoaAddress] = useState<`0x${string}` | null>(null);
@@ -186,7 +317,7 @@ export function HypurrConnectProvider({
186
317
  const eoaSignerRef = useRef<EoaSigner | null>(null);
187
318
 
188
319
  // ── Derived auth ─────────────────────────────────────────────
189
- const authMethod: AuthMethod = tgLoginData
320
+ const authMethod: AuthMethod = tgAuthToken
190
321
  ? "telegram"
191
322
  : eoaAddress
192
323
  ? "eoa"
@@ -244,14 +375,20 @@ export function HypurrConnectProvider({
244
375
  try {
245
376
  const [{ response: twapResp }, { response: scaleResp }] =
246
377
  await Promise.all([
247
- tgClient.hyperliquidWalletTwapSessions({
248
- authData: authDataMap,
249
- walletId: selectedWalletId,
250
- }),
251
- tgClient.hyperliquidWalletScaleSessions({
252
- authData: authDataMap,
253
- walletId: selectedWalletId,
254
- }),
378
+ tgClient.hyperliquidWalletTwapSessions(
379
+ {
380
+ authData: {},
381
+ walletId: selectedWalletId,
382
+ },
383
+ telegramRpcOptions,
384
+ ),
385
+ tgClient.hyperliquidWalletScaleSessions(
386
+ {
387
+ authData: {},
388
+ walletId: selectedWalletId,
389
+ },
390
+ telegramRpcOptions,
391
+ ),
255
392
  ]);
256
393
  if (cancelled) return;
257
394
  setWallets((prev) =>
@@ -281,19 +418,28 @@ export function HypurrConnectProvider({
281
418
  cancelled = true;
282
419
  clearInterval(id);
283
420
  };
284
- }, [authMethod, selectedWalletId, pollInterval, tgClient, authDataMap]);
421
+ }, [
422
+ authMethod,
423
+ selectedWalletId,
424
+ pollInterval,
425
+ tgClient,
426
+ telegramRpcOptions,
427
+ ]);
285
428
 
286
429
  const user = useMemo<HypurrUser | null>(() => {
287
- if (tgLoginData && authMethod === "telegram" && selectedWallet) {
430
+ if (tgAuthToken && authMethod === "telegram" && selectedWallet && tgUser) {
431
+ const mediaUrl = normalizeMediaUrl(config.mediaUrl);
288
432
  return {
289
433
  address: selectedWallet.ethereumAddress,
290
434
  walletId: selectedWallet.id,
291
- displayName: tgLoginData.username
292
- ? `@${tgLoginData.username}`
293
- : tgLoginData.first_name,
294
- photoUrl: tgLoginData.photo_url,
435
+ displayName: tgUser.telegramUsername
436
+ ? `@${tgUser.telegramUsername}`
437
+ : `Telegram ${tgUser.telegramId}`,
438
+ photoUrl: tgUser.pictureFileId
439
+ ? `${mediaUrl}/${tgUser.pictureFileId}`
440
+ : undefined,
295
441
  authMethod: "telegram",
296
- telegramId: String(tgLoginData.id),
442
+ telegramId: String(tgUser.telegramId),
297
443
  hfunScore: tgUser?.reputation?.hfunScore,
298
444
  reputationScore: tgUser?.reputation?.reputationScore,
299
445
  };
@@ -307,7 +453,14 @@ export function HypurrConnectProvider({
307
453
  };
308
454
  }
309
455
  return null;
310
- }, [tgLoginData, selectedWallet, eoaAddress, authMethod, tgUser]);
456
+ }, [
457
+ tgAuthToken,
458
+ selectedWallet,
459
+ eoaAddress,
460
+ authMethod,
461
+ tgUser,
462
+ config.mediaUrl,
463
+ ]);
311
464
 
312
465
  // ── Exchange client ──────────────────────────────────────────
313
466
  // Telegram: GrpcExchangeTransport → HyperliquidCoreAction (server signs)
@@ -350,7 +503,7 @@ export function HypurrConnectProvider({
350
503
  const transport = new GrpcExchangeTransport({
351
504
  isTestnet: config.isTestnet ?? false,
352
505
  telegramClient: tgClient,
353
- authDataMap,
506
+ rpcOptions: telegramRpcOptions,
354
507
  walletId: user.walletId,
355
508
  onAuthError: () => onInvalidAuthRef.current?.(),
356
509
  });
@@ -435,7 +588,8 @@ export function HypurrConnectProvider({
435
588
  const { privateKey, address: agentAddress } =
436
589
  await generateAgentKey();
437
590
 
438
- const chainIdHex = `0x${signer.chainId.toString(16)}` as `0x${string}`;
591
+ const chainIdHex =
592
+ `0x${signer.chainId.toString(16)}` as `0x${string}`;
439
593
  const nonce = Date.now();
440
594
  const action = {
441
595
  type: "approveAgent" as const,
@@ -567,7 +721,7 @@ export function HypurrConnectProvider({
567
721
  eoaAddress,
568
722
  config.isTestnet,
569
723
  tgClient,
570
- authDataMap,
724
+ telegramRpcOptions,
571
725
  ]);
572
726
 
573
727
  const handleClearAgent = useCallback(() => {
@@ -580,43 +734,52 @@ export function HypurrConnectProvider({
580
734
  // ── Wallet management (Telegram only) ───────────────────────
581
735
  const createWallet = useCallback(
582
736
  async (name: string): Promise<HyperliquidWallet> => {
583
- const { response } = await tgClient.hyperliquidWalletCreate({
584
- authData: authDataMap,
585
- name,
586
- });
737
+ const { response } = await tgClient.hyperliquidWalletCreate(
738
+ {
739
+ authData: {},
740
+ name,
741
+ },
742
+ telegramRpcOptions,
743
+ );
587
744
  refreshWallets();
588
745
  if (!response.wallet)
589
746
  throw new Error("Wallet creation returned no wallet");
590
747
  return response.wallet;
591
748
  },
592
- [tgClient, authDataMap, refreshWallets],
749
+ [tgClient, telegramRpcOptions, refreshWallets],
593
750
  );
594
751
 
595
752
  const deleteWallet = useCallback(
596
753
  async (walletId: number): Promise<void> => {
597
- await tgClient.hyperliquidWalletDelete({
598
- authData: authDataMap,
599
- walletId,
600
- });
754
+ await tgClient.hyperliquidWalletDelete(
755
+ {
756
+ authData: {},
757
+ walletId,
758
+ },
759
+ telegramRpcOptions,
760
+ );
601
761
  if (walletId === selectedWalletId) {
602
762
  const remaining = wallets.filter((w) => w.id !== walletId);
603
763
  setSelectedWalletId(remaining[0]?.id ?? 0);
604
764
  }
605
765
  refreshWallets();
606
766
  },
607
- [tgClient, authDataMap, selectedWalletId, wallets, refreshWallets],
767
+ [tgClient, telegramRpcOptions, selectedWalletId, wallets, refreshWallets],
608
768
  );
609
769
 
610
770
  const createWalletPack = useCallback(
611
771
  async (name: string): Promise<number> => {
612
- const { response } = await tgClient.telegramChatWalletPackCreate({
613
- authData: authDataMap,
614
- name,
615
- });
772
+ const { response } = await tgClient.telegramChatWalletPackCreate(
773
+ {
774
+ authData: {},
775
+ name,
776
+ },
777
+ telegramRpcOptions,
778
+ );
616
779
  refreshWallets();
617
780
  return response.packId;
618
781
  },
619
- [tgClient, authDataMap, refreshWallets],
782
+ [tgClient, telegramRpcOptions, refreshWallets],
620
783
  );
621
784
 
622
785
  const addPackLabel = useCallback(
@@ -625,13 +788,16 @@ export function HypurrConnectProvider({
625
788
  walletLabel: string;
626
789
  packId: number;
627
790
  }): Promise<void> => {
628
- await tgClient.telegramChatWalletPackLabelAdd({
629
- authData: authDataMap,
630
- ...params,
631
- });
791
+ await tgClient.telegramChatWalletPackLabelAdd(
792
+ {
793
+ authData: {},
794
+ ...params,
795
+ },
796
+ telegramRpcOptions,
797
+ );
632
798
  refreshWallets();
633
799
  },
634
- [tgClient, authDataMap, refreshWallets],
800
+ [tgClient, telegramRpcOptions, refreshWallets],
635
801
  );
636
802
 
637
803
  const modifyPackLabel = useCallback(
@@ -640,24 +806,30 @@ export function HypurrConnectProvider({
640
806
  walletLabelNew: string;
641
807
  packId: number;
642
808
  }): Promise<void> => {
643
- await tgClient.telegramChatWalletPackLabelModify({
644
- authData: authDataMap,
645
- ...params,
646
- });
809
+ await tgClient.telegramChatWalletPackLabelModify(
810
+ {
811
+ authData: {},
812
+ ...params,
813
+ },
814
+ telegramRpcOptions,
815
+ );
647
816
  refreshWallets();
648
817
  },
649
- [tgClient, authDataMap, refreshWallets],
818
+ [tgClient, telegramRpcOptions, refreshWallets],
650
819
  );
651
820
 
652
821
  const removePackLabel = useCallback(
653
822
  async (params: { walletLabel: string; packId: number }): Promise<void> => {
654
- await tgClient.telegramChatWalletPackLabelRemove({
655
- authData: authDataMap,
656
- ...params,
657
- });
823
+ await tgClient.telegramChatWalletPackLabelRemove(
824
+ {
825
+ authData: {},
826
+ ...params,
827
+ },
828
+ telegramRpcOptions,
829
+ );
658
830
  refreshWallets();
659
831
  },
660
- [tgClient, authDataMap, refreshWallets],
832
+ [tgClient, telegramRpcOptions, refreshWallets],
661
833
  );
662
834
 
663
835
  // ── TWAP session management (Telegram only) ─────────────────
@@ -668,11 +840,14 @@ export function HypurrConnectProvider({
668
840
  "authData" | "walletId"
669
841
  >,
670
842
  ) => {
671
- const { response } = await tgClient.hyperliquidTwapCreate({
672
- authData: authDataMap,
673
- walletId: selectedWalletId,
674
- ...params,
675
- });
843
+ const { response } = await tgClient.hyperliquidTwapCreate(
844
+ {
845
+ authData: {},
846
+ walletId: selectedWalletId,
847
+ ...params,
848
+ },
849
+ telegramRpcOptions,
850
+ );
676
851
  if (!response.session)
677
852
  throw new Error("TWAP creation returned no session");
678
853
  const session = response.session;
@@ -685,7 +860,7 @@ export function HypurrConnectProvider({
685
860
  );
686
861
  return session;
687
862
  },
688
- [tgClient, authDataMap, selectedWalletId],
863
+ [tgClient, telegramRpcOptions, selectedWalletId],
689
864
  );
690
865
 
691
866
  const modifyTwap = useCallback(
@@ -695,13 +870,15 @@ export function HypurrConnectProvider({
695
870
  "authData" | "walletId"
696
871
  >,
697
872
  ) => {
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");
873
+ const { response } = await tgClient.hyperliquidTwapModify(
874
+ {
875
+ authData: {},
876
+ walletId: selectedWalletId,
877
+ ...params,
878
+ },
879
+ telegramRpcOptions,
880
+ );
881
+ if (!response.session) throw new Error("TWAP modify returned no session");
705
882
  const session = response.session;
706
883
  setWallets((prev) =>
707
884
  prev.map((w) =>
@@ -717,16 +894,19 @@ export function HypurrConnectProvider({
717
894
  );
718
895
  return session;
719
896
  },
720
- [tgClient, authDataMap, selectedWalletId],
897
+ [tgClient, telegramRpcOptions, selectedWalletId],
721
898
  );
722
899
 
723
900
  const cancelTwap = useCallback(
724
901
  async (sessionId: number) => {
725
- await tgClient.hyperliquidTwapCancel({
726
- authData: authDataMap,
727
- walletId: selectedWalletId,
728
- twapSessionId: sessionId,
729
- });
902
+ await tgClient.hyperliquidTwapCancel(
903
+ {
904
+ authData: {},
905
+ walletId: selectedWalletId,
906
+ twapSessionId: sessionId,
907
+ },
908
+ telegramRpcOptions,
909
+ );
730
910
  setWallets((prev) =>
731
911
  prev.map((w) =>
732
912
  w.id === selectedWalletId
@@ -738,7 +918,7 @@ export function HypurrConnectProvider({
738
918
  ),
739
919
  );
740
920
  },
741
- [tgClient, authDataMap, selectedWalletId],
921
+ [tgClient, telegramRpcOptions, selectedWalletId],
742
922
  );
743
923
 
744
924
  // ── Scale session management (Telegram only) ───────────────
@@ -749,11 +929,14 @@ export function HypurrConnectProvider({
749
929
  "authData" | "walletId"
750
930
  >,
751
931
  ) => {
752
- const { response } = await tgClient.hyperliquidScaleCreate({
753
- authData: authDataMap,
754
- walletId: selectedWalletId,
755
- ...params,
756
- });
932
+ const { response } = await tgClient.hyperliquidScaleCreate(
933
+ {
934
+ authData: {},
935
+ walletId: selectedWalletId,
936
+ ...params,
937
+ },
938
+ telegramRpcOptions,
939
+ );
757
940
  if (!response.session)
758
941
  throw new Error("Scale creation returned no session");
759
942
  const session = response.session;
@@ -766,16 +949,19 @@ export function HypurrConnectProvider({
766
949
  );
767
950
  return session;
768
951
  },
769
- [tgClient, authDataMap, selectedWalletId],
952
+ [tgClient, telegramRpcOptions, selectedWalletId],
770
953
  );
771
954
 
772
955
  const cancelScale = useCallback(
773
956
  async (sessionId: number) => {
774
- await tgClient.hyperliquidScaleCancel({
775
- authData: authDataMap,
776
- walletId: selectedWalletId,
777
- scaleSessionId: sessionId,
778
- });
957
+ await tgClient.hyperliquidScaleCancel(
958
+ {
959
+ authData: {},
960
+ walletId: selectedWalletId,
961
+ scaleSessionId: sessionId,
962
+ },
963
+ telegramRpcOptions,
964
+ );
779
965
  setWallets((prev) =>
780
966
  prev.map((w) =>
781
967
  w.id === selectedWalletId
@@ -789,7 +975,7 @@ export function HypurrConnectProvider({
789
975
  ),
790
976
  );
791
977
  },
792
- [tgClient, authDataMap, selectedWalletId],
978
+ [tgClient, telegramRpcOptions, selectedWalletId],
793
979
  );
794
980
 
795
981
  // ── Login modal state ────────────────────────────────────────
@@ -798,23 +984,60 @@ export function HypurrConnectProvider({
798
984
  const closeLoginModal = useCallback(() => setLoginModalOpen(false), []);
799
985
 
800
986
  // ── 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
- }, []);
987
+ const loginTelegram = useCallback(() => {
988
+ const state = randomState();
989
+ sessionStorage.setItem(TELEGRAM_AUTH_STATE_KEY, state);
990
+
991
+ const configuredReturnTo = config.telegram.returnTo;
992
+ const returnTo =
993
+ typeof configuredReturnTo === "function"
994
+ ? configuredReturnTo()
995
+ : configuredReturnTo || currentReturnTo();
996
+
997
+ const authUrl = new URL(config.telegram.authHubUrl || DEFAULT_AUTH_HUB_URL);
998
+ authUrl.searchParams.set("return_to", returnTo);
999
+ authUrl.searchParams.set("state", state);
1000
+ authUrl.searchParams.set("scope", normalizeScopes(config.telegram.scope));
1001
+
1002
+ const width = 520;
1003
+ const height = 720;
1004
+ const left = window.screenX + Math.max(0, (window.outerWidth - width) / 2);
1005
+ const top = window.screenY + Math.max(0, (window.outerHeight - height) / 2);
1006
+ const popup = window.open(
1007
+ authUrl.toString(),
1008
+ "hypurr_telegram_auth",
1009
+ [
1010
+ `width=${width}`,
1011
+ `height=${height}`,
1012
+ `left=${Math.round(left)}`,
1013
+ `top=${Math.round(top)}`,
1014
+ "resizable=yes",
1015
+ "scrollbars=yes",
1016
+ ].join(","),
1017
+ );
1018
+
1019
+ if (popup) {
1020
+ popup.focus();
1021
+ return;
1022
+ }
1023
+
1024
+ window.location.assign(authUrl.toString());
1025
+ }, [
1026
+ config.telegram.authHubUrl,
1027
+ config.telegram.returnTo,
1028
+ config.telegram.scope,
1029
+ ]);
808
1030
 
809
1031
  const connectEoa = useCallback(
810
1032
  (address: `0x${string}`, signer?: EoaSigner) => {
811
1033
  eoaSignerRef.current = signer ?? null;
812
1034
  setEoaAddress(address);
813
- setTgLoginData(null);
1035
+ setTgAuthToken(null);
814
1036
  setTgUser(null);
815
1037
  setTgError(null);
816
1038
  setEoaError(null);
817
1039
  localStorage.removeItem(TELEGRAM_STORAGE_KEY);
1040
+ localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
818
1041
 
819
1042
  const existing = loadAgent(address);
820
1043
  if (existing && existing.validUntil > Date.now()) {
@@ -932,14 +1155,15 @@ export function HypurrConnectProvider({
932
1155
  );
933
1156
 
934
1157
  const logout = useCallback(() => {
935
- setTgLoginData(null);
936
1158
  setTgUser(null);
937
1159
  setTgError(null);
1160
+ setTgAuthToken(null);
938
1161
  setEoaAddress(null);
939
1162
  setAgent(null);
940
1163
  setEoaError(null);
941
1164
  eoaSignerRef.current = null;
942
1165
  localStorage.removeItem(TELEGRAM_STORAGE_KEY);
1166
+ localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
943
1167
  }, []);
944
1168
 
945
1169
  // ── Context value ────────────────────────────────────────────
@@ -991,6 +1215,8 @@ export function HypurrConnectProvider({
991
1215
  useWidget: config.telegram?.useWidget ?? false,
992
1216
 
993
1217
  authDataMap,
1218
+ authToken: tgAuthToken,
1219
+ telegramRpcOptions,
994
1220
  telegramClient: tgClient,
995
1221
  staticClient,
996
1222
  }),
@@ -1032,6 +1258,8 @@ export function HypurrConnectProvider({
1032
1258
  config.telegram?.botUsername,
1033
1259
  config.telegram?.useWidget,
1034
1260
  authDataMap,
1261
+ tgAuthToken,
1262
+ telegramRpcOptions,
1035
1263
  tgClient,
1036
1264
  staticClient,
1037
1265
  ],