@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.
- package/README.md +81 -64
- package/dist/index.d.ts +23 -15
- package/dist/index.js +346 -257
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/GrpcExchangeTransport.ts +16 -10
- package/src/HypurrConnectProvider.tsx +357 -129
- package/src/LoginModal.tsx +15 -74
- package/src/grpc.ts +2 -2
- package/src/index.ts +0 -2
- package/src/types.ts +29 -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,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: (
|
|
51
|
+
loginTelegram: () => void;
|
|
49
52
|
botUsername: string;
|
|
50
53
|
useWidget: boolean;
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
const TELEGRAM_STORAGE_KEY = "hypurr-connect-tg-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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(
|
|
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 [
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
126
|
-
|
|
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(
|
|
135
|
-
|
|
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
|
-
|
|
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
|
-
}, [
|
|
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 =
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
}, [
|
|
421
|
+
}, [
|
|
422
|
+
authMethod,
|
|
423
|
+
selectedWalletId,
|
|
424
|
+
pollInterval,
|
|
425
|
+
tgClient,
|
|
426
|
+
telegramRpcOptions,
|
|
427
|
+
]);
|
|
285
428
|
|
|
286
429
|
const user = useMemo<HypurrUser | null>(() => {
|
|
287
|
-
if (
|
|
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:
|
|
292
|
-
? `@${
|
|
293
|
-
:
|
|
294
|
-
photoUrl:
|
|
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(
|
|
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
|
-
}, [
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
585
|
-
|
|
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,
|
|
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
|
-
|
|
599
|
-
|
|
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,
|
|
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
|
-
|
|
614
|
-
|
|
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,
|
|
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
|
-
|
|
630
|
-
|
|
631
|
-
|
|
791
|
+
await tgClient.telegramChatWalletPackLabelAdd(
|
|
792
|
+
{
|
|
793
|
+
authData: {},
|
|
794
|
+
...params,
|
|
795
|
+
},
|
|
796
|
+
telegramRpcOptions,
|
|
797
|
+
);
|
|
632
798
|
refreshWallets();
|
|
633
799
|
},
|
|
634
|
-
[tgClient,
|
|
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
|
-
|
|
645
|
-
|
|
646
|
-
|
|
809
|
+
await tgClient.telegramChatWalletPackLabelModify(
|
|
810
|
+
{
|
|
811
|
+
authData: {},
|
|
812
|
+
...params,
|
|
813
|
+
},
|
|
814
|
+
telegramRpcOptions,
|
|
815
|
+
);
|
|
647
816
|
refreshWallets();
|
|
648
817
|
},
|
|
649
|
-
[tgClient,
|
|
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
|
-
|
|
656
|
-
|
|
657
|
-
|
|
823
|
+
await tgClient.telegramChatWalletPackLabelRemove(
|
|
824
|
+
{
|
|
825
|
+
authData: {},
|
|
826
|
+
...params,
|
|
827
|
+
},
|
|
828
|
+
telegramRpcOptions,
|
|
829
|
+
);
|
|
658
830
|
refreshWallets();
|
|
659
831
|
},
|
|
660
|
-
[tgClient,
|
|
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
|
-
|
|
673
|
-
|
|
674
|
-
|
|
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,
|
|
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
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
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,
|
|
897
|
+
[tgClient, telegramRpcOptions, selectedWalletId],
|
|
721
898
|
);
|
|
722
899
|
|
|
723
900
|
const cancelTwap = useCallback(
|
|
724
901
|
async (sessionId: number) => {
|
|
725
|
-
await tgClient.hyperliquidTwapCancel(
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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,
|
|
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
|
-
|
|
754
|
-
|
|
755
|
-
|
|
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,
|
|
952
|
+
[tgClient, telegramRpcOptions, selectedWalletId],
|
|
770
953
|
);
|
|
771
954
|
|
|
772
955
|
const cancelScale = useCallback(
|
|
773
956
|
async (sessionId: number) => {
|
|
774
|
-
await tgClient.hyperliquidScaleCancel(
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
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,
|
|
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((
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
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
|
-
|
|
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
|
],
|