@hfunlabs/hypurr-connect 0.1.0 → 0.1.2

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/dist/index.js CHANGED
@@ -1,5 +1,9 @@
1
1
  // src/HypurrConnectProvider.tsx
2
- import { ExchangeClient, HttpTransport, InfoClient } from "@hfunlabs/hyperliquid";
2
+ import {
3
+ ExchangeClient,
4
+ HttpTransport
5
+ } from "@hfunlabs/hyperliquid";
6
+ import { approveAgent as sdkApproveAgent } from "@hfunlabs/hyperliquid/api/exchange";
3
7
  import { PrivateKeySigner } from "@hfunlabs/hyperliquid/signing";
4
8
  import {
5
9
  createContext,
@@ -7,10 +11,12 @@ import {
7
11
  useContext,
8
12
  useEffect,
9
13
  useMemo,
14
+ useRef,
10
15
  useState
11
16
  } from "react";
12
17
 
13
18
  // src/agent.ts
19
+ var AGENT_NAME = "hypurr-connect";
14
20
  var AGENT_STORAGE_PREFIX = "hypurr-connect-agent";
15
21
  function storageKey(masterAddress) {
16
22
  return `${AGENT_STORAGE_PREFIX}:${masterAddress.toLowerCase()}`;
@@ -37,6 +43,39 @@ async function generateAgentKey() {
37
43
  const signer = new PrivateKeySigner2(privateKey);
38
44
  return { privateKey, address: signer.address };
39
45
  }
46
+ async function fetchActiveAgent(userAddress, isTestnet) {
47
+ const url = isTestnet ? "https://api.hyperliquid-testnet.xyz/info" : "https://api.hyperliquid.xyz/info";
48
+ const res = await fetch(url, {
49
+ method: "POST",
50
+ headers: { "Content-Type": "application/json" },
51
+ body: JSON.stringify({ type: "extraAgents", user: userAddress })
52
+ });
53
+ if (!res.ok) return null;
54
+ const agents = await res.json();
55
+ if (!Array.isArray(agents)) return null;
56
+ const nowMs = Date.now();
57
+ const match = agents.find(
58
+ (a) => a.name === AGENT_NAME && a.validUntil * 1e3 > nowMs
59
+ );
60
+ if (!match) return null;
61
+ return { ...match, validUntil: match.validUntil * 1e3 };
62
+ }
63
+ async function isAgentValid(stored, userAddress, isTestnet) {
64
+ if (stored.validUntil <= Date.now()) return false;
65
+ const remote = await fetchActiveAgent(userAddress, isTestnet);
66
+ if (!remote) return false;
67
+ return remote.address.toLowerCase() === stored.address.toLowerCase() && remote.validUntil > Date.now();
68
+ }
69
+ var DEAD_AGENT_PATTERNS = [
70
+ /agent address .+ is not valid/i,
71
+ /unknown signer/i,
72
+ /not authorized/i,
73
+ /not an agent/i
74
+ ];
75
+ function isDeadAgentError(err) {
76
+ const msg = err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : String(err);
77
+ return DEAD_AGENT_PATTERNS.some((p) => p.test(msg));
78
+ }
40
79
 
41
80
  // src/grpc.ts
42
81
  import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
@@ -148,6 +187,14 @@ function useHypurrConnect() {
148
187
  );
149
188
  return ctx;
150
189
  }
190
+ function useHypurrConnectInternal() {
191
+ const ctx = useContext(HypurrConnectContext);
192
+ if (!ctx)
193
+ throw new Error(
194
+ "useHypurrConnectInternal must be used within <HypurrConnectProvider>"
195
+ );
196
+ return ctx;
197
+ }
151
198
  function HypurrConnectProvider({
152
199
  config,
153
200
  children
@@ -171,6 +218,7 @@ function HypurrConnectProvider({
171
218
  () => tgLoginData ? toAuthDataMap(tgLoginData) : {},
172
219
  [tgLoginData]
173
220
  );
221
+ const [tgUserTick, setTgUserTick] = useState(0);
174
222
  useEffect(() => {
175
223
  if (!tgLoginData) return;
176
224
  let cancelled = false;
@@ -179,9 +227,7 @@ function HypurrConnectProvider({
179
227
  (async () => {
180
228
  try {
181
229
  const authData = toAuthDataMap(tgLoginData);
182
- console.log(authData);
183
230
  const { response } = await tgClient.telegramUser({ authData });
184
- console.log(response);
185
231
  if (cancelled) return;
186
232
  setTgUser(response.user ?? null);
187
233
  } catch (err) {
@@ -195,27 +241,55 @@ function HypurrConnectProvider({
195
241
  return () => {
196
242
  cancelled = true;
197
243
  };
198
- }, [tgLoginData, tgClient]);
244
+ }, [tgLoginData, tgClient, tgUserTick]);
199
245
  const [eoaAddress, setEoaAddress] = useState(null);
200
246
  const [agent, setAgent] = useState(null);
247
+ const [eoaLoading, setEoaLoading] = useState(false);
248
+ const [eoaError, setEoaError] = useState(null);
249
+ const authMethod = tgLoginData ? "telegram" : eoaAddress ? "eoa" : null;
250
+ const [wallets, setWallets] = useState([]);
251
+ const [selectedWalletId, setSelectedWalletId] = useState(0);
252
+ const [packs, setPacks] = useState([]);
253
+ const refreshWallets = useCallback(() => setTgUserTick((t) => t + 1), []);
201
254
  useEffect(() => {
202
- if (eoaAddress) {
203
- setAgent(loadAgent(eoaAddress));
204
- } else {
205
- setAgent(null);
255
+ if (authMethod !== "telegram" || !tgUser) {
256
+ setWallets([]);
257
+ setSelectedWalletId(0);
258
+ setPacks([]);
259
+ return;
206
260
  }
207
- }, [eoaAddress]);
208
- const authMethod = tgLoginData ? "telegram" : eoaAddress ? "eoa" : null;
209
- const tgWallet = tgUser?.wallet ?? (tgUser?.wallets ?? [])[0] ?? null;
261
+ const userWallets = tgUser.wallets ?? [];
262
+ setWallets(userWallets);
263
+ setPacks(tgUser.packs ?? []);
264
+ const defaultId = tgUser.walletId || userWallets[0]?.id || 0;
265
+ setSelectedWalletId((prev) => {
266
+ if (prev && userWallets.some((w) => w.id === prev)) return prev;
267
+ return defaultId;
268
+ });
269
+ }, [authMethod, tgUser]);
270
+ const selectedWallet = useMemo(
271
+ () => wallets.find((w) => w.id === selectedWalletId) ?? wallets[0] ?? null,
272
+ [wallets, selectedWalletId]
273
+ );
274
+ const selectWallet = useCallback(
275
+ (walletId) => {
276
+ if (wallets.some((w) => w.id === walletId)) {
277
+ setSelectedWalletId(walletId);
278
+ }
279
+ },
280
+ [wallets]
281
+ );
210
282
  const user = useMemo(() => {
211
- if (tgLoginData && authMethod === "telegram") {
283
+ if (tgLoginData && authMethod === "telegram" && selectedWallet) {
212
284
  return {
213
- address: tgWallet?.ethereumAddress ?? "",
214
- walletId: tgUser?.walletId ?? tgWallet?.id ?? 0,
285
+ address: selectedWallet.ethereumAddress,
286
+ walletId: selectedWallet.id,
215
287
  displayName: tgLoginData.username ? `@${tgLoginData.username}` : tgLoginData.first_name,
216
288
  photoUrl: tgLoginData.photo_url,
217
289
  authMethod: "telegram",
218
- telegramId: String(tgLoginData.id)
290
+ telegramId: String(tgLoginData.id),
291
+ hfunScore: tgUser?.reputation?.hfunScore,
292
+ reputationScore: tgUser?.reputation?.reputationScore
219
293
  };
220
294
  }
221
295
  if (eoaAddress && authMethod === "eoa") {
@@ -227,7 +301,16 @@ function HypurrConnectProvider({
227
301
  };
228
302
  }
229
303
  return null;
230
- }, [tgLoginData, tgUser, tgWallet, eoaAddress, authMethod]);
304
+ }, [tgLoginData, selectedWallet, eoaAddress, authMethod, tgUser]);
305
+ const onDeadAgentRef = useRef(
306
+ null
307
+ );
308
+ onDeadAgentRef.current = (addr) => {
309
+ clearAgent(addr);
310
+ setAgent(null);
311
+ setEoaError("Agent expired or was deregistered. Please reconnect.");
312
+ };
313
+ const agentReady = authMethod === "telegram" || authMethod === "eoa" && !!agent;
231
314
  const exchange = useMemo(() => {
232
315
  if (authMethod === "telegram" && user?.address) {
233
316
  const transport = new GrpcExchangeTransport({
@@ -242,127 +325,129 @@ function HypurrConnectProvider({
242
325
  userAddress: user.address
243
326
  });
244
327
  }
245
- if (authMethod === "eoa" && agent) {
328
+ if (authMethod === "eoa" && eoaAddress) {
329
+ if (!agent) {
330
+ const noAgentTransport = {
331
+ isTestnet: config.isTestnet ?? false,
332
+ request() {
333
+ throw new Error(
334
+ "[HypurrConnect] No agent key approved. Call approveAgent(signTypedDataAsync) before using the exchange client. This is required for EOA wallets to sign transactions on Hyperliquid."
335
+ );
336
+ }
337
+ };
338
+ return new ExchangeClient({
339
+ transport: noAgentTransport,
340
+ externalSigning: true,
341
+ userAddress: eoaAddress
342
+ });
343
+ }
344
+ const inner = new HttpTransport({
345
+ isTestnet: config.isTestnet ?? false
346
+ });
347
+ const deadAgentAddr = eoaAddress;
348
+ const guardedTransport = {
349
+ isTestnet: inner.isTestnet,
350
+ async request(endpoint, payload, signal) {
351
+ try {
352
+ return await inner.request(endpoint, payload, signal);
353
+ } catch (err) {
354
+ if (endpoint === "exchange" && isDeadAgentError(err)) {
355
+ onDeadAgentRef.current?.(deadAgentAddr);
356
+ }
357
+ throw err;
358
+ }
359
+ }
360
+ };
246
361
  const wallet = new PrivateKeySigner(agent.privateKey);
247
362
  return new ExchangeClient({
248
- transport: new HttpTransport({
249
- isTestnet: config.isTestnet ?? false
250
- }),
363
+ transport: guardedTransport,
251
364
  wallet
252
365
  });
253
366
  }
254
367
  return null;
255
- }, [authMethod, user, agent, config.isTestnet, tgClient, authDataMap]);
256
- const infoClient = useMemo(
257
- () => new InfoClient({
258
- transport: new HttpTransport({
259
- isTestnet: config.isTestnet ?? false
260
- })
261
- }),
262
- [config.isTestnet]
263
- );
264
- const [usdcBalance, setUsdcBalance] = useState(null);
265
- const [usdcBalanceLoading, setUsdcBalanceLoading] = useState(false);
266
- const [balanceTick, setBalanceTick] = useState(0);
267
- const refreshBalance = useCallback(() => setBalanceTick((t) => t + 1), []);
268
- useEffect(() => {
269
- const addr = user?.address;
270
- if (!addr) {
271
- setUsdcBalance(null);
272
- return;
273
- }
274
- let cancelled = false;
275
- setUsdcBalanceLoading(true);
276
- (async () => {
277
- try {
278
- const state = await infoClient.clearinghouseState({
279
- user: addr
280
- });
281
- if (!cancelled) {
282
- setUsdcBalance(state.withdrawable);
283
- }
284
- } catch (err) {
285
- console.error("[HypurrConnect] Failed to fetch USDC balance:", err);
286
- if (!cancelled) setUsdcBalance(null);
287
- } finally {
288
- if (!cancelled) setUsdcBalanceLoading(false);
289
- }
290
- })();
291
- return () => {
292
- cancelled = true;
293
- };
294
- }, [user?.address, infoClient, balanceTick]);
295
- const approveAgent = useCallback(
296
- async (signTypedDataAsync) => {
297
- if (!eoaAddress) throw new Error("No EOA address connected");
298
- const { privateKey, address: agentAddress } = await generateAgentKey();
299
- const isTestnet = config.isTestnet ?? false;
300
- const nonce = Date.now();
301
- const action = {
302
- type: "approveAgent",
303
- signatureChainId: isTestnet ? "0x66eee" : "0xa4b1",
304
- hyperliquidChain: isTestnet ? "Testnet" : "Mainnet",
305
- agentAddress: agentAddress.toLowerCase(),
306
- agentName: null,
307
- nonce
308
- };
309
- const types = {
310
- "HyperliquidTransaction:ApproveAgent": [
311
- { name: "hyperliquidChain", type: "string" },
312
- { name: "agentAddress", type: "address" },
313
- { name: "agentName", type: "string" },
314
- { name: "nonce", type: "uint64" }
315
- ]
316
- };
317
- const signature = await signTypedDataAsync({
318
- domain: {
319
- name: "HyperliquidSignTransaction",
320
- version: "1",
321
- chainId: isTestnet ? 421614 : 42161,
322
- verifyingContract: "0x0000000000000000000000000000000000000000"
323
- },
324
- types,
325
- primaryType: "HyperliquidTransaction:ApproveAgent",
326
- message: {
327
- hyperliquidChain: action.hyperliquidChain,
328
- agentAddress: action.agentAddress,
329
- agentName: "",
330
- nonce: BigInt(nonce)
331
- }
332
- });
333
- const r = `0x${signature.slice(2, 66)}`;
334
- const s = `0x${signature.slice(66, 130)}`;
335
- const v = parseInt(signature.slice(130, 132), 16);
336
- const url = isTestnet ? "https://api.hyperliquid-testnet.xyz/exchange" : "https://api.hyperliquid.xyz/exchange";
337
- const res = await fetch(url, {
338
- method: "POST",
339
- headers: { "Content-Type": "application/json" },
340
- body: JSON.stringify({
341
- action,
342
- nonce,
343
- signature: { r, s, v }
344
- })
345
- });
346
- const body = await res.json();
347
- if (body?.status !== "ok") {
348
- throw new Error(`approveAgent failed: ${JSON.stringify(body)}`);
349
- }
350
- const stored = {
351
- privateKey,
352
- address: agentAddress,
353
- approvedAt: Date.now()
354
- };
355
- saveAgent(eoaAddress, stored);
356
- setAgent(stored);
357
- },
358
- [eoaAddress, config.isTestnet]
359
- );
368
+ }, [
369
+ authMethod,
370
+ user,
371
+ agent,
372
+ eoaAddress,
373
+ config.isTestnet,
374
+ tgClient,
375
+ authDataMap
376
+ ]);
360
377
  const handleClearAgent = useCallback(() => {
361
378
  if (eoaAddress) {
362
379
  clearAgent(eoaAddress);
363
380
  setAgent(null);
364
381
  }
365
382
  }, [eoaAddress]);
383
+ const createWallet = useCallback(
384
+ async (name) => {
385
+ const { response } = await tgClient.hyperliquidWalletCreate({
386
+ authData: authDataMap,
387
+ name
388
+ });
389
+ refreshWallets();
390
+ if (!response.wallet)
391
+ throw new Error("Wallet creation returned no wallet");
392
+ return response.wallet;
393
+ },
394
+ [tgClient, authDataMap, refreshWallets]
395
+ );
396
+ const deleteWallet = useCallback(
397
+ async (walletId) => {
398
+ await tgClient.hyperliquidWalletDelete({
399
+ authData: authDataMap,
400
+ walletId
401
+ });
402
+ if (walletId === selectedWalletId) {
403
+ const remaining = wallets.filter((w) => w.id !== walletId);
404
+ setSelectedWalletId(remaining[0]?.id ?? 0);
405
+ }
406
+ refreshWallets();
407
+ },
408
+ [tgClient, authDataMap, selectedWalletId, wallets, refreshWallets]
409
+ );
410
+ const createWalletPack = useCallback(
411
+ async (name) => {
412
+ const { response } = await tgClient.telegramChatWalletPackCreate({
413
+ authData: authDataMap,
414
+ name
415
+ });
416
+ refreshWallets();
417
+ return response.packId;
418
+ },
419
+ [tgClient, authDataMap, refreshWallets]
420
+ );
421
+ const addPackLabel = useCallback(
422
+ async (params) => {
423
+ await tgClient.telegramChatWalletPackLabelAdd({
424
+ authData: authDataMap,
425
+ ...params
426
+ });
427
+ refreshWallets();
428
+ },
429
+ [tgClient, authDataMap, refreshWallets]
430
+ );
431
+ const modifyPackLabel = useCallback(
432
+ async (params) => {
433
+ await tgClient.telegramChatWalletPackLabelModify({
434
+ authData: authDataMap,
435
+ ...params
436
+ });
437
+ refreshWallets();
438
+ },
439
+ [tgClient, authDataMap, refreshWallets]
440
+ );
441
+ const removePackLabel = useCallback(
442
+ async (params) => {
443
+ await tgClient.telegramChatWalletPackLabelRemove({
444
+ authData: authDataMap,
445
+ ...params
446
+ });
447
+ refreshWallets();
448
+ },
449
+ [tgClient, authDataMap, refreshWallets]
450
+ );
366
451
  const [loginModalOpen, setLoginModalOpen] = useState(false);
367
452
  const openLoginModal = useCallback(() => setLoginModalOpen(true), []);
368
453
  const closeLoginModal = useCallback(() => setLoginModalOpen(false), []);
@@ -371,42 +456,115 @@ function HypurrConnectProvider({
371
456
  localStorage.setItem(TELEGRAM_STORAGE_KEY, JSON.stringify(data));
372
457
  setEoaAddress(null);
373
458
  setAgent(null);
459
+ setEoaError(null);
374
460
  }, []);
375
- const loginEoa = useCallback((address) => {
461
+ const connectEoa = useCallback((address) => {
376
462
  setEoaAddress(address);
377
463
  setTgLoginData(null);
378
464
  setTgUser(null);
379
465
  setTgError(null);
466
+ setEoaError(null);
380
467
  localStorage.removeItem(TELEGRAM_STORAGE_KEY);
468
+ const existing = loadAgent(address);
469
+ if (existing && existing.validUntil > Date.now()) {
470
+ setAgent(existing);
471
+ } else {
472
+ if (existing) clearAgent(address);
473
+ setAgent(null);
474
+ }
381
475
  }, []);
476
+ const approveAgentFn = useCallback(
477
+ async (signTypedDataAsync, chainId) => {
478
+ if (!eoaAddress) {
479
+ throw new Error(
480
+ "[HypurrConnect] Cannot approve agent: no EOA wallet connected. Call connectEoa(address) first."
481
+ );
482
+ }
483
+ setEoaLoading(true);
484
+ setEoaError(null);
485
+ try {
486
+ const existing = loadAgent(eoaAddress);
487
+ if (existing) {
488
+ const isTestnet2 = config.isTestnet ?? false;
489
+ const valid = await isAgentValid(existing, eoaAddress, isTestnet2);
490
+ if (valid) {
491
+ setAgent(existing);
492
+ return;
493
+ }
494
+ clearAgent(eoaAddress);
495
+ }
496
+ const { privateKey, address: agentAddress } = await generateAgentKey();
497
+ const isTestnet = config.isTestnet ?? false;
498
+ const wallet = {
499
+ signTypedData: signTypedDataAsync,
500
+ getAddresses: async () => [eoaAddress],
501
+ getChainId: async () => chainId
502
+ };
503
+ const transport = new HttpTransport({ isTestnet });
504
+ await sdkApproveAgent(
505
+ { transport, wallet },
506
+ {
507
+ agentAddress: agentAddress.toLowerCase(),
508
+ agentName: AGENT_NAME
509
+ }
510
+ );
511
+ const remote = await fetchActiveAgent(eoaAddress, isTestnet);
512
+ const validUntil = remote?.validUntil ?? Date.now() + 7 * 24 * 60 * 60 * 1e3;
513
+ const stored = {
514
+ privateKey,
515
+ address: agentAddress,
516
+ approvedAt: Date.now(),
517
+ validUntil
518
+ };
519
+ saveAgent(eoaAddress, stored);
520
+ setAgent(stored);
521
+ } catch (err) {
522
+ console.error("[HypurrConnect] EOA agent approval failed:", err);
523
+ setEoaError(err instanceof Error ? err.message : String(err));
524
+ setAgent(null);
525
+ } finally {
526
+ setEoaLoading(false);
527
+ }
528
+ },
529
+ [eoaAddress, config.isTestnet]
530
+ );
382
531
  const logout = useCallback(() => {
383
532
  setTgLoginData(null);
384
533
  setTgUser(null);
385
534
  setTgError(null);
386
535
  setEoaAddress(null);
387
536
  setAgent(null);
537
+ setEoaError(null);
388
538
  localStorage.removeItem(TELEGRAM_STORAGE_KEY);
389
539
  }, []);
390
540
  const value = useMemo(
391
541
  () => ({
392
542
  user,
393
543
  isLoggedIn: !!user,
394
- isLoading: tgLoading,
395
- error: tgError,
544
+ isLoading: tgLoading || eoaLoading,
545
+ error: tgError ?? eoaError,
396
546
  authMethod,
397
547
  exchange,
398
- usdcBalance,
399
- usdcBalanceLoading,
400
- refreshBalance,
548
+ wallets,
549
+ selectedWalletId,
550
+ selectWallet,
551
+ createWallet,
552
+ deleteWallet,
553
+ refreshWallets,
554
+ packs,
555
+ createWalletPack,
556
+ addPackLabel,
557
+ modifyPackLabel,
558
+ removePackLabel,
401
559
  loginModalOpen,
402
560
  openLoginModal,
403
561
  closeLoginModal,
404
562
  loginTelegram,
405
- loginEoa,
563
+ connectEoa,
564
+ approveAgent: approveAgentFn,
406
565
  logout,
407
566
  agent,
408
- agentReady: authMethod === "telegram" || !!agent,
409
- approveAgent,
567
+ agentReady,
410
568
  clearAgent: handleClearAgent,
411
569
  botId: config.telegram?.botId ?? "",
412
570
  authDataMap,
@@ -416,20 +574,31 @@ function HypurrConnectProvider({
416
574
  [
417
575
  user,
418
576
  tgLoading,
577
+ eoaLoading,
419
578
  tgError,
579
+ eoaError,
420
580
  authMethod,
421
581
  exchange,
422
- usdcBalance,
423
- usdcBalanceLoading,
424
- refreshBalance,
582
+ wallets,
583
+ selectedWalletId,
584
+ selectWallet,
585
+ createWallet,
586
+ deleteWallet,
587
+ refreshWallets,
588
+ packs,
589
+ createWalletPack,
590
+ addPackLabel,
591
+ modifyPackLabel,
592
+ removePackLabel,
425
593
  loginModalOpen,
426
594
  openLoginModal,
427
595
  closeLoginModal,
428
596
  loginTelegram,
429
- loginEoa,
597
+ connectEoa,
598
+ approveAgentFn,
430
599
  logout,
431
600
  agent,
432
- approveAgent,
601
+ agentReady,
433
602
  handleClearAgent,
434
603
  config.telegram?.botId,
435
604
  authDataMap,
@@ -454,18 +623,18 @@ import {
454
623
 
455
624
  // src/icons/MetaMaskColorIcon.tsx
456
625
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
457
- function MetaMaskColorIcon({ className }) {
626
+ function MetaMaskColorIcon({ style }) {
458
627
  return /* @__PURE__ */ jsxs(
459
628
  "svg",
460
629
  {
461
630
  width: "24",
462
631
  height: "24",
463
632
  viewBox: "0 0 24 24",
464
- className,
633
+ style,
465
634
  fill: "none",
466
635
  xmlns: "http://www.w3.org/2000/svg",
467
636
  children: [
468
- /* @__PURE__ */ jsxs("g", { "clip-path": "url(#clip0_2567_1088)", children: [
637
+ /* @__PURE__ */ jsxs("g", { clipPath: "url(#clip0_2567_1088)", children: [
469
638
  /* @__PURE__ */ jsx2(
470
639
  "path",
471
640
  {
@@ -532,14 +701,14 @@ function MetaMaskColorIcon({ className }) {
532
701
 
533
702
  // src/icons/TelegramColorIcon.tsx
534
703
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
535
- function TelegramColorIcon({ className }) {
704
+ function TelegramColorIcon({ style }) {
536
705
  return /* @__PURE__ */ jsxs2(
537
706
  "svg",
538
707
  {
539
708
  width: "24",
540
709
  height: "24",
541
710
  viewBox: "0 0 24 24",
542
- className,
711
+ style,
543
712
  fill: "none",
544
713
  xmlns: "http://www.w3.org/2000/svg",
545
714
  children: [
@@ -553,8 +722,8 @@ function TelegramColorIcon({ className }) {
553
722
  /* @__PURE__ */ jsx3(
554
723
  "path",
555
724
  {
556
- "fill-rule": "evenodd",
557
- "clip-rule": "evenodd",
725
+ fillRule: "evenodd",
726
+ clipRule: "evenodd",
558
727
  d: "M7.07426 11.905C9.69794 10.7619 11.4475 10.0083 12.3229 9.64417C14.8222 8.60458 15.3416 8.424 15.6801 8.41803C15.7546 8.41672 15.921 8.43517 16.0289 8.52267C16.1199 8.59655 16.145 8.69635 16.1569 8.7664C16.1689 8.83645 16.1839 8.99602 16.172 9.1207C16.0366 10.5438 15.4505 13.9973 15.1523 15.5912C15.0262 16.2657 14.7778 16.4918 14.5373 16.514C14.0146 16.562 13.6178 16.1686 13.1115 15.8367C12.3194 15.3175 11.8719 14.9943 11.103 14.4876C10.2145 13.902 10.7905 13.5802 11.2969 13.0542C11.4294 12.9166 13.7322 10.822 13.7768 10.632C13.7824 10.6082 13.7875 10.5196 13.7349 10.4729C13.6823 10.4261 13.6046 10.4421 13.5486 10.4548C13.4691 10.4728 12.2037 11.3092 9.75232 12.964C9.39313 13.2106 9.06779 13.3308 8.7763 13.3245C8.45496 13.3176 7.83681 13.1428 7.37729 12.9934C6.81366 12.8102 6.3657 12.7134 6.40471 12.4022C6.42503 12.2401 6.64821 12.0744 7.07426 11.905Z",
559
728
  fill: "white"
560
729
  }
@@ -569,8 +738,8 @@ function TelegramColorIcon({ className }) {
569
738
  y2: "1789.65",
570
739
  gradientUnits: "userSpaceOnUse",
571
740
  children: [
572
- /* @__PURE__ */ jsx3("stop", { "stop-color": "#2AABEE" }),
573
- /* @__PURE__ */ jsx3("stop", { offset: "1", "stop-color": "#229ED9" })
741
+ /* @__PURE__ */ jsx3("stop", { stopColor: "#2AABEE" }),
742
+ /* @__PURE__ */ jsx3("stop", { offset: "1", stopColor: "#229ED9" })
574
743
  ]
575
744
  }
576
745
  ) })
@@ -582,7 +751,67 @@ function TelegramColorIcon({ className }) {
582
751
  // src/LoginModal.tsx
583
752
  import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
584
753
  var MOBILE_BREAKPOINT = 640;
585
- var btnClass = "flex h-[53px] w-full items-center gap-3 overflow-hidden rounded bg-white/5 px-6 text-sm font-semibold tracking-tight text-white cursor-pointer transition-colors duration-150 hover:bg-white/10";
754
+ var btnStyle = {
755
+ display: "flex",
756
+ height: 53,
757
+ width: "100%",
758
+ alignItems: "center",
759
+ gap: 12,
760
+ overflow: "hidden",
761
+ borderRadius: 6,
762
+ background: "rgba(255,255,255,0.05)",
763
+ padding: "0 24px",
764
+ fontSize: 14,
765
+ fontWeight: 600,
766
+ letterSpacing: "-0.01em",
767
+ color: "#fff",
768
+ cursor: "pointer",
769
+ border: "none",
770
+ transition: "background 150ms"
771
+ };
772
+ var btnHoverBg = { background: "rgba(255,255,255,0.1)" };
773
+ var backdropStyle = {
774
+ position: "fixed",
775
+ inset: 0,
776
+ zIndex: 100,
777
+ background: "rgba(0,0,0,0.6)",
778
+ backdropFilter: "blur(2px)",
779
+ WebkitBackdropFilter: "blur(2px)"
780
+ };
781
+ var modalWrapperStyle = {
782
+ position: "fixed",
783
+ inset: 0,
784
+ zIndex: 101,
785
+ display: "flex",
786
+ alignItems: "center",
787
+ justifyContent: "center",
788
+ padding: 16
789
+ };
790
+ var modalBoxStyle = {
791
+ display: "flex",
792
+ width: 400,
793
+ flexDirection: "column",
794
+ alignItems: "center",
795
+ gap: 16,
796
+ overflow: "hidden",
797
+ borderRadius: 12,
798
+ border: "1px solid rgba(255,255,255,0.1)",
799
+ background: "#282828",
800
+ padding: 24
801
+ };
802
+ var headingStyle = {
803
+ fontSize: 16,
804
+ fontWeight: 700,
805
+ letterSpacing: "-0.025em",
806
+ color: "#fff",
807
+ margin: 0
808
+ };
809
+ var dividerStyle = {
810
+ height: 1,
811
+ width: "100%",
812
+ background: "rgba(255,255,255,0.05)"
813
+ };
814
+ var iconSize = { width: 20, height: 20 };
586
815
  var mobileQuery = typeof window !== "undefined" ? window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`) : null;
587
816
  function subscribeMobile(cb) {
588
817
  mobileQuery?.addEventListener("change", cb);
@@ -594,8 +823,23 @@ function getSnapshotMobile() {
594
823
  function useIsMobile() {
595
824
  return useSyncExternalStore(subscribeMobile, getSnapshotMobile, () => false);
596
825
  }
826
+ function HoverButton({
827
+ onClick,
828
+ children
829
+ }) {
830
+ return /* @__PURE__ */ jsx4(
831
+ motion.button,
832
+ {
833
+ type: "button",
834
+ onClick,
835
+ style: btnStyle,
836
+ whileHover: btnHoverBg,
837
+ children
838
+ }
839
+ );
840
+ }
597
841
  function LoginModal({ onConnectWallet, walletIcon }) {
598
- const { loginTelegram, loginModalOpen, closeLoginModal, botId } = useHypurrConnect();
842
+ const { loginTelegram, loginModalOpen, closeLoginModal, botId } = useHypurrConnectInternal();
599
843
  const handleTelegramAuth = useCallback2(
600
844
  (user) => {
601
845
  loginTelegram(user);
@@ -642,22 +886,33 @@ function LoginModal({ onConnectWallet, walletIcon }) {
642
886
  }, [botId]);
643
887
  const isMobile = useIsMobile();
644
888
  const modalContent = /* @__PURE__ */ jsxs3(Fragment, { children: [
645
- /* @__PURE__ */ jsx4("div", { className: "flex w-full flex-col items-center gap-2 overflow-hidden", children: /* @__PURE__ */ jsxs3("button", { type: "button", onClick: openTelegramOAuth, className: btnClass, children: [
646
- /* @__PURE__ */ jsx4(TelegramColorIcon, { className: "size-5" }),
647
- "Telegram"
648
- ] }) }),
649
- /* @__PURE__ */ jsx4("div", { className: "h-px w-full bg-white/5" }),
889
+ /* @__PURE__ */ jsx4(
890
+ "div",
891
+ {
892
+ style: {
893
+ display: "flex",
894
+ width: "100%",
895
+ flexDirection: "column",
896
+ alignItems: "center",
897
+ gap: 8,
898
+ overflow: "hidden"
899
+ },
900
+ children: /* @__PURE__ */ jsxs3(HoverButton, { onClick: openTelegramOAuth, children: [
901
+ /* @__PURE__ */ jsx4(TelegramColorIcon, { style: iconSize }),
902
+ "Telegram"
903
+ ] })
904
+ }
905
+ ),
906
+ /* @__PURE__ */ jsx4("div", { style: dividerStyle }),
650
907
  /* @__PURE__ */ jsxs3(
651
- "button",
908
+ HoverButton,
652
909
  {
653
- type: "button",
654
910
  onClick: () => {
655
911
  closeLoginModal();
656
912
  onConnectWallet();
657
913
  },
658
- className: btnClass,
659
914
  children: [
660
- walletIcon ?? /* @__PURE__ */ jsx4(MetaMaskColorIcon, { className: "size-5" }),
915
+ walletIcon ?? /* @__PURE__ */ jsx4(MetaMaskColorIcon, { style: iconSize }),
661
916
  "Wallet"
662
917
  ]
663
918
  }
@@ -667,7 +922,7 @@ function LoginModal({ onConnectWallet, walletIcon }) {
667
922
  /* @__PURE__ */ jsx4(
668
923
  motion.div,
669
924
  {
670
- className: "fixed inset-0 z-[100] bg-black/60 backdrop-blur-[2px]",
925
+ style: backdropStyle,
671
926
  initial: { opacity: 0 },
672
927
  animate: { opacity: 1 },
673
928
  exit: { opacity: 0 },
@@ -679,7 +934,7 @@ function LoginModal({ onConnectWallet, walletIcon }) {
679
934
  /* @__PURE__ */ jsx4(
680
935
  motion.div,
681
936
  {
682
- className: "fixed inset-0 z-[101] flex items-center justify-center p-4",
937
+ style: modalWrapperStyle,
683
938
  initial: { opacity: 0 },
684
939
  animate: { opacity: 1 },
685
940
  exit: { opacity: 0 },
@@ -688,14 +943,14 @@ function LoginModal({ onConnectWallet, walletIcon }) {
688
943
  children: /* @__PURE__ */ jsxs3(
689
944
  motion.div,
690
945
  {
691
- className: "flex w-[400px] flex-col items-center gap-4 overflow-hidden rounded-xl border border-white/10 bg-[#282828] p-6",
946
+ style: modalBoxStyle,
692
947
  initial: { opacity: 0, scale: 0.95, y: 10 },
693
948
  animate: { opacity: 1, scale: 1, y: 0 },
694
949
  exit: { opacity: 0, scale: 0.95, y: 10 },
695
950
  transition: { duration: 0.2, ease: "easeOut" },
696
951
  onClick: (e) => e.stopPropagation(),
697
952
  children: [
698
- /* @__PURE__ */ jsx4("p", { className: "text-base font-bold tracking-tight text-white", children: "Connect" }),
953
+ /* @__PURE__ */ jsx4("p", { style: headingStyle, children: "Connect" }),
699
954
  modalContent
700
955
  ]
701
956
  }
@@ -705,6 +960,47 @@ function LoginModal({ onConnectWallet, walletIcon }) {
705
960
  )
706
961
  ] })) });
707
962
  }
963
+ var drawerSheetStyle = {
964
+ position: "fixed",
965
+ left: 0,
966
+ right: 0,
967
+ bottom: 0,
968
+ zIndex: 101,
969
+ display: "flex",
970
+ flexDirection: "column",
971
+ alignItems: "center",
972
+ gap: 16,
973
+ borderTopLeftRadius: 12,
974
+ borderTopRightRadius: 12,
975
+ borderLeft: "1px solid rgba(255,255,255,0.1)",
976
+ borderRight: "1px solid rgba(255,255,255,0.1)",
977
+ borderTop: "1px solid rgba(255,255,255,0.1)",
978
+ background: "#282828",
979
+ padding: "12px 24px max(24px, env(safe-area-inset-bottom))"
980
+ };
981
+ var drawerBgStyle = {
982
+ position: "absolute",
983
+ left: 0,
984
+ right: 0,
985
+ top: 0,
986
+ bottom: "-100vh",
987
+ zIndex: -1,
988
+ background: "#282828",
989
+ borderTopLeftRadius: 12,
990
+ borderTopRightRadius: 12
991
+ };
992
+ var grabHandleAreaStyle = {
993
+ width: "100%",
994
+ cursor: "grab",
995
+ paddingBottom: 4
996
+ };
997
+ var grabHandleStyle = {
998
+ margin: "0 auto",
999
+ height: 4,
1000
+ width: 100,
1001
+ borderRadius: 9999,
1002
+ background: "rgba(255,255,255,0.05)"
1003
+ };
708
1004
  function MobileDrawer({
709
1005
  children,
710
1006
  onClose
@@ -724,7 +1020,7 @@ function MobileDrawer({
724
1020
  /* @__PURE__ */ jsx4(
725
1021
  motion.div,
726
1022
  {
727
- className: "fixed inset-0 z-[100] bg-black/60 backdrop-blur-[2px]",
1023
+ style: backdropStyle,
728
1024
  initial: { opacity: 0 },
729
1025
  animate: { opacity: 1 },
730
1026
  exit: { opacity: 0 },
@@ -736,7 +1032,7 @@ function MobileDrawer({
736
1032
  /* @__PURE__ */ jsxs3(
737
1033
  motion.div,
738
1034
  {
739
- className: "fixed inset-x-0 bottom-0 z-[101] flex flex-col items-center gap-4 rounded-t-xl border-x border-t border-white/10 bg-[#282828] px-6 pb-[max(24px,env(safe-area-inset-bottom))] pt-3",
1035
+ style: drawerSheetStyle,
740
1036
  initial: { y: "100%" },
741
1037
  animate: { y: 0 },
742
1038
  exit: { y: "100%" },
@@ -746,9 +1042,9 @@ function MobileDrawer({
746
1042
  dragElastic: { top: 0, bottom: 0.4 },
747
1043
  onDragEnd: handleDragEnd,
748
1044
  children: [
749
- /* @__PURE__ */ jsx4("div", { className: "absolute inset-x-0 top-0 bottom-[-100vh] -z-10 bg-[#282828] rounded-t-xl" }),
750
- /* @__PURE__ */ jsx4("div", { className: "w-full cursor-grab pt-0 pb-1 active:cursor-grabbing", children: /* @__PURE__ */ jsx4("div", { className: "mx-auto h-1 w-[100px] rounded-full bg-white/5" }) }),
751
- /* @__PURE__ */ jsx4("p", { className: "text-base font-bold tracking-tight text-white", children: "Connect" }),
1045
+ /* @__PURE__ */ jsx4("div", { style: drawerBgStyle }),
1046
+ /* @__PURE__ */ jsx4("div", { style: grabHandleAreaStyle, children: /* @__PURE__ */ jsx4("div", { style: grabHandleStyle }) }),
1047
+ /* @__PURE__ */ jsx4("p", { style: headingStyle, children: "Connect" }),
752
1048
  children
753
1049
  ]
754
1050
  },