@hfunlabs/hypurr-connect 0.1.0
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 +363 -0
- package/dist/index.d.ts +113 -0
- package/dist/index.js +767 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
- package/src/GrpcExchangeTransport.ts +122 -0
- package/src/HypurrConnectProvider.tsx +418 -0
- package/src/LoginModal.tsx +230 -0
- package/src/agent.ts +43 -0
- package/src/grpc.ts +23 -0
- package/src/icons/MetaMaskColorIcon.tsx +53 -0
- package/src/icons/TelegramColorIcon.tsx +36 -0
- package/src/index.ts +18 -0
- package/src/types.ts +100 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
// src/HypurrConnectProvider.tsx
|
|
2
|
+
import { ExchangeClient, HttpTransport, InfoClient } from "@hfunlabs/hyperliquid";
|
|
3
|
+
import { PrivateKeySigner } from "@hfunlabs/hyperliquid/signing";
|
|
4
|
+
import {
|
|
5
|
+
createContext,
|
|
6
|
+
useCallback,
|
|
7
|
+
useContext,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useState
|
|
11
|
+
} from "react";
|
|
12
|
+
|
|
13
|
+
// src/agent.ts
|
|
14
|
+
var AGENT_STORAGE_PREFIX = "hypurr-connect-agent";
|
|
15
|
+
function storageKey(masterAddress) {
|
|
16
|
+
return `${AGENT_STORAGE_PREFIX}:${masterAddress.toLowerCase()}`;
|
|
17
|
+
}
|
|
18
|
+
function loadAgent(masterAddress) {
|
|
19
|
+
try {
|
|
20
|
+
const raw = localStorage.getItem(storageKey(masterAddress));
|
|
21
|
+
return raw ? JSON.parse(raw) : null;
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function saveAgent(masterAddress, agent) {
|
|
27
|
+
localStorage.setItem(storageKey(masterAddress), JSON.stringify(agent));
|
|
28
|
+
}
|
|
29
|
+
function clearAgent(masterAddress) {
|
|
30
|
+
localStorage.removeItem(storageKey(masterAddress));
|
|
31
|
+
}
|
|
32
|
+
async function generateAgentKey() {
|
|
33
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
34
|
+
const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
35
|
+
const privateKey = `0x${hex}`;
|
|
36
|
+
const { PrivateKeySigner: PrivateKeySigner2 } = await import("@hfunlabs/hyperliquid/signing");
|
|
37
|
+
const signer = new PrivateKeySigner2(privateKey);
|
|
38
|
+
return { privateKey, address: signer.address };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/grpc.ts
|
|
42
|
+
import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
|
|
43
|
+
import { StaticClient } from "hypurr-grpc/ts/hypurr/static/static_service.client";
|
|
44
|
+
import { TelegramClient } from "hypurr-grpc/ts/hypurr/telegram/telegram_service.client";
|
|
45
|
+
var GRPC_URL = "https://grpc.hypurr.fun";
|
|
46
|
+
function createTransport(config) {
|
|
47
|
+
return new GrpcWebFetchTransport({
|
|
48
|
+
baseUrl: GRPC_URL,
|
|
49
|
+
timeout: config.grpcTimeout ?? 15e3
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function createTelegramClient(config) {
|
|
53
|
+
return new TelegramClient(createTransport(config));
|
|
54
|
+
}
|
|
55
|
+
function createStaticClient(config) {
|
|
56
|
+
return new StaticClient(createTransport(config));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/GrpcExchangeTransport.ts
|
|
60
|
+
var GrpcExchangeTransport = class {
|
|
61
|
+
isTestnet;
|
|
62
|
+
telegramClient;
|
|
63
|
+
authDataMap;
|
|
64
|
+
walletId;
|
|
65
|
+
infoUrl;
|
|
66
|
+
constructor(config) {
|
|
67
|
+
this.isTestnet = config.isTestnet ?? false;
|
|
68
|
+
this.telegramClient = config.telegramClient;
|
|
69
|
+
this.authDataMap = config.authDataMap;
|
|
70
|
+
this.walletId = config.walletId;
|
|
71
|
+
this.infoUrl = this.isTestnet ? "https://api.hyperliquid-testnet.xyz" : "https://api.hyperliquid.xyz";
|
|
72
|
+
}
|
|
73
|
+
async request(endpoint, payload, signal) {
|
|
74
|
+
if (endpoint === "exchange") {
|
|
75
|
+
return this.exchangeViaGrpc(payload, signal);
|
|
76
|
+
}
|
|
77
|
+
return this.directRequest(endpoint, payload, signal);
|
|
78
|
+
}
|
|
79
|
+
async exchangeViaGrpc(payload, signal) {
|
|
80
|
+
if (signal?.aborted) {
|
|
81
|
+
throw new DOMException("Request aborted", "AbortError");
|
|
82
|
+
}
|
|
83
|
+
const actionBytes = new TextEncoder().encode(
|
|
84
|
+
JSON.stringify(payload.action)
|
|
85
|
+
);
|
|
86
|
+
console.debug("[GrpcExchangeTransport] sending action:", payload.action);
|
|
87
|
+
const { response } = await this.telegramClient.hyperliquidCoreAction({
|
|
88
|
+
authData: this.authDataMap,
|
|
89
|
+
walletId: this.walletId,
|
|
90
|
+
action: actionBytes,
|
|
91
|
+
nonce: payload.nonce || Date.now()
|
|
92
|
+
});
|
|
93
|
+
if (signal?.aborted) {
|
|
94
|
+
throw new DOMException("Request aborted", "AbortError");
|
|
95
|
+
}
|
|
96
|
+
const { status, result, error } = response;
|
|
97
|
+
console.debug(
|
|
98
|
+
"[GrpcExchangeTransport] gRPC status:",
|
|
99
|
+
status,
|
|
100
|
+
"error:",
|
|
101
|
+
error
|
|
102
|
+
);
|
|
103
|
+
if (error) {
|
|
104
|
+
throw new Error(`GrpcExchangeTransport: ${error}`);
|
|
105
|
+
}
|
|
106
|
+
if (result && result.length > 0) {
|
|
107
|
+
const parsed = JSON.parse(new TextDecoder().decode(result));
|
|
108
|
+
console.debug("[GrpcExchangeTransport] parsed result:", parsed);
|
|
109
|
+
return parsed;
|
|
110
|
+
}
|
|
111
|
+
return { status: "ok", response: status };
|
|
112
|
+
}
|
|
113
|
+
async directRequest(endpoint, payload, signal) {
|
|
114
|
+
const res = await fetch(`${this.infoUrl}/${endpoint}`, {
|
|
115
|
+
method: "POST",
|
|
116
|
+
headers: { "Content-Type": "application/json" },
|
|
117
|
+
body: JSON.stringify(payload),
|
|
118
|
+
signal
|
|
119
|
+
});
|
|
120
|
+
if (!res.ok) {
|
|
121
|
+
throw new Error(`HTTP ${res.status}: ${await res.text()}`);
|
|
122
|
+
}
|
|
123
|
+
return await res.json();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/HypurrConnectProvider.tsx
|
|
128
|
+
import { jsx } from "react/jsx-runtime";
|
|
129
|
+
var TELEGRAM_STORAGE_KEY = "hypurr-connect-tg-user";
|
|
130
|
+
function toAuthDataMap(data) {
|
|
131
|
+
const map = {
|
|
132
|
+
id: String(data.id),
|
|
133
|
+
first_name: data.first_name,
|
|
134
|
+
auth_date: String(data.auth_date),
|
|
135
|
+
hash: data.hash
|
|
136
|
+
};
|
|
137
|
+
if (data.last_name) map.last_name = data.last_name;
|
|
138
|
+
if (data.username) map.username = data.username;
|
|
139
|
+
if (data.photo_url) map.photo_url = data.photo_url;
|
|
140
|
+
return map;
|
|
141
|
+
}
|
|
142
|
+
var HypurrConnectContext = createContext(null);
|
|
143
|
+
function useHypurrConnect() {
|
|
144
|
+
const ctx = useContext(HypurrConnectContext);
|
|
145
|
+
if (!ctx)
|
|
146
|
+
throw new Error(
|
|
147
|
+
"useHypurrConnect must be used within <HypurrConnectProvider>"
|
|
148
|
+
);
|
|
149
|
+
return ctx;
|
|
150
|
+
}
|
|
151
|
+
function HypurrConnectProvider({
|
|
152
|
+
config,
|
|
153
|
+
children
|
|
154
|
+
}) {
|
|
155
|
+
const tgClient = useMemo(() => createTelegramClient(config), [config]);
|
|
156
|
+
const staticClient = useMemo(() => createStaticClient(config), [config]);
|
|
157
|
+
const [tgLoginData, setTgLoginData] = useState(
|
|
158
|
+
() => {
|
|
159
|
+
try {
|
|
160
|
+
const stored = localStorage.getItem(TELEGRAM_STORAGE_KEY);
|
|
161
|
+
return stored ? JSON.parse(stored) : null;
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
const [tgUser, setTgUser] = useState(null);
|
|
168
|
+
const [tgLoading, setTgLoading] = useState(false);
|
|
169
|
+
const [tgError, setTgError] = useState(null);
|
|
170
|
+
const authDataMap = useMemo(
|
|
171
|
+
() => tgLoginData ? toAuthDataMap(tgLoginData) : {},
|
|
172
|
+
[tgLoginData]
|
|
173
|
+
);
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
if (!tgLoginData) return;
|
|
176
|
+
let cancelled = false;
|
|
177
|
+
setTgLoading(true);
|
|
178
|
+
setTgError(null);
|
|
179
|
+
(async () => {
|
|
180
|
+
try {
|
|
181
|
+
const authData = toAuthDataMap(tgLoginData);
|
|
182
|
+
console.log(authData);
|
|
183
|
+
const { response } = await tgClient.telegramUser({ authData });
|
|
184
|
+
console.log(response);
|
|
185
|
+
if (cancelled) return;
|
|
186
|
+
setTgUser(response.user ?? null);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
if (cancelled) return;
|
|
189
|
+
console.error("[HypurrConnect] gRPC TelegramUser failed:", err);
|
|
190
|
+
setTgError(err instanceof Error ? err.message : String(err));
|
|
191
|
+
} finally {
|
|
192
|
+
if (!cancelled) setTgLoading(false);
|
|
193
|
+
}
|
|
194
|
+
})();
|
|
195
|
+
return () => {
|
|
196
|
+
cancelled = true;
|
|
197
|
+
};
|
|
198
|
+
}, [tgLoginData, tgClient]);
|
|
199
|
+
const [eoaAddress, setEoaAddress] = useState(null);
|
|
200
|
+
const [agent, setAgent] = useState(null);
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
if (eoaAddress) {
|
|
203
|
+
setAgent(loadAgent(eoaAddress));
|
|
204
|
+
} else {
|
|
205
|
+
setAgent(null);
|
|
206
|
+
}
|
|
207
|
+
}, [eoaAddress]);
|
|
208
|
+
const authMethod = tgLoginData ? "telegram" : eoaAddress ? "eoa" : null;
|
|
209
|
+
const tgWallet = tgUser?.wallet ?? (tgUser?.wallets ?? [])[0] ?? null;
|
|
210
|
+
const user = useMemo(() => {
|
|
211
|
+
if (tgLoginData && authMethod === "telegram") {
|
|
212
|
+
return {
|
|
213
|
+
address: tgWallet?.ethereumAddress ?? "",
|
|
214
|
+
walletId: tgUser?.walletId ?? tgWallet?.id ?? 0,
|
|
215
|
+
displayName: tgLoginData.username ? `@${tgLoginData.username}` : tgLoginData.first_name,
|
|
216
|
+
photoUrl: tgLoginData.photo_url,
|
|
217
|
+
authMethod: "telegram",
|
|
218
|
+
telegramId: String(tgLoginData.id)
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
if (eoaAddress && authMethod === "eoa") {
|
|
222
|
+
return {
|
|
223
|
+
address: eoaAddress,
|
|
224
|
+
walletId: 0,
|
|
225
|
+
displayName: `${eoaAddress.slice(0, 6)}...${eoaAddress.slice(-4)}`,
|
|
226
|
+
authMethod: "eoa"
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}, [tgLoginData, tgUser, tgWallet, eoaAddress, authMethod]);
|
|
231
|
+
const exchange = useMemo(() => {
|
|
232
|
+
if (authMethod === "telegram" && user?.address) {
|
|
233
|
+
const transport = new GrpcExchangeTransport({
|
|
234
|
+
isTestnet: config.isTestnet ?? false,
|
|
235
|
+
telegramClient: tgClient,
|
|
236
|
+
authDataMap,
|
|
237
|
+
walletId: user.walletId
|
|
238
|
+
});
|
|
239
|
+
return new ExchangeClient({
|
|
240
|
+
transport,
|
|
241
|
+
externalSigning: true,
|
|
242
|
+
userAddress: user.address
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (authMethod === "eoa" && agent) {
|
|
246
|
+
const wallet = new PrivateKeySigner(agent.privateKey);
|
|
247
|
+
return new ExchangeClient({
|
|
248
|
+
transport: new HttpTransport({
|
|
249
|
+
isTestnet: config.isTestnet ?? false
|
|
250
|
+
}),
|
|
251
|
+
wallet
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
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
|
+
);
|
|
360
|
+
const handleClearAgent = useCallback(() => {
|
|
361
|
+
if (eoaAddress) {
|
|
362
|
+
clearAgent(eoaAddress);
|
|
363
|
+
setAgent(null);
|
|
364
|
+
}
|
|
365
|
+
}, [eoaAddress]);
|
|
366
|
+
const [loginModalOpen, setLoginModalOpen] = useState(false);
|
|
367
|
+
const openLoginModal = useCallback(() => setLoginModalOpen(true), []);
|
|
368
|
+
const closeLoginModal = useCallback(() => setLoginModalOpen(false), []);
|
|
369
|
+
const loginTelegram = useCallback((data) => {
|
|
370
|
+
setTgLoginData(data);
|
|
371
|
+
localStorage.setItem(TELEGRAM_STORAGE_KEY, JSON.stringify(data));
|
|
372
|
+
setEoaAddress(null);
|
|
373
|
+
setAgent(null);
|
|
374
|
+
}, []);
|
|
375
|
+
const loginEoa = useCallback((address) => {
|
|
376
|
+
setEoaAddress(address);
|
|
377
|
+
setTgLoginData(null);
|
|
378
|
+
setTgUser(null);
|
|
379
|
+
setTgError(null);
|
|
380
|
+
localStorage.removeItem(TELEGRAM_STORAGE_KEY);
|
|
381
|
+
}, []);
|
|
382
|
+
const logout = useCallback(() => {
|
|
383
|
+
setTgLoginData(null);
|
|
384
|
+
setTgUser(null);
|
|
385
|
+
setTgError(null);
|
|
386
|
+
setEoaAddress(null);
|
|
387
|
+
setAgent(null);
|
|
388
|
+
localStorage.removeItem(TELEGRAM_STORAGE_KEY);
|
|
389
|
+
}, []);
|
|
390
|
+
const value = useMemo(
|
|
391
|
+
() => ({
|
|
392
|
+
user,
|
|
393
|
+
isLoggedIn: !!user,
|
|
394
|
+
isLoading: tgLoading,
|
|
395
|
+
error: tgError,
|
|
396
|
+
authMethod,
|
|
397
|
+
exchange,
|
|
398
|
+
usdcBalance,
|
|
399
|
+
usdcBalanceLoading,
|
|
400
|
+
refreshBalance,
|
|
401
|
+
loginModalOpen,
|
|
402
|
+
openLoginModal,
|
|
403
|
+
closeLoginModal,
|
|
404
|
+
loginTelegram,
|
|
405
|
+
loginEoa,
|
|
406
|
+
logout,
|
|
407
|
+
agent,
|
|
408
|
+
agentReady: authMethod === "telegram" || !!agent,
|
|
409
|
+
approveAgent,
|
|
410
|
+
clearAgent: handleClearAgent,
|
|
411
|
+
botId: config.telegram?.botId ?? "",
|
|
412
|
+
authDataMap,
|
|
413
|
+
telegramClient: tgClient,
|
|
414
|
+
staticClient
|
|
415
|
+
}),
|
|
416
|
+
[
|
|
417
|
+
user,
|
|
418
|
+
tgLoading,
|
|
419
|
+
tgError,
|
|
420
|
+
authMethod,
|
|
421
|
+
exchange,
|
|
422
|
+
usdcBalance,
|
|
423
|
+
usdcBalanceLoading,
|
|
424
|
+
refreshBalance,
|
|
425
|
+
loginModalOpen,
|
|
426
|
+
openLoginModal,
|
|
427
|
+
closeLoginModal,
|
|
428
|
+
loginTelegram,
|
|
429
|
+
loginEoa,
|
|
430
|
+
logout,
|
|
431
|
+
agent,
|
|
432
|
+
approveAgent,
|
|
433
|
+
handleClearAgent,
|
|
434
|
+
config.telegram?.botId,
|
|
435
|
+
authDataMap,
|
|
436
|
+
tgClient,
|
|
437
|
+
staticClient
|
|
438
|
+
]
|
|
439
|
+
);
|
|
440
|
+
return /* @__PURE__ */ jsx(HypurrConnectContext.Provider, { value, children });
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// src/LoginModal.tsx
|
|
444
|
+
import {
|
|
445
|
+
AnimatePresence,
|
|
446
|
+
motion,
|
|
447
|
+
useAnimationControls
|
|
448
|
+
} from "framer-motion";
|
|
449
|
+
import {
|
|
450
|
+
useCallback as useCallback2,
|
|
451
|
+
useEffect as useEffect2,
|
|
452
|
+
useSyncExternalStore
|
|
453
|
+
} from "react";
|
|
454
|
+
|
|
455
|
+
// src/icons/MetaMaskColorIcon.tsx
|
|
456
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
457
|
+
function MetaMaskColorIcon({ className }) {
|
|
458
|
+
return /* @__PURE__ */ jsxs(
|
|
459
|
+
"svg",
|
|
460
|
+
{
|
|
461
|
+
width: "24",
|
|
462
|
+
height: "24",
|
|
463
|
+
viewBox: "0 0 24 24",
|
|
464
|
+
className,
|
|
465
|
+
fill: "none",
|
|
466
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
467
|
+
children: [
|
|
468
|
+
/* @__PURE__ */ jsxs("g", { "clip-path": "url(#clip0_2567_1088)", children: [
|
|
469
|
+
/* @__PURE__ */ jsx2(
|
|
470
|
+
"path",
|
|
471
|
+
{
|
|
472
|
+
d: "M19.8188 19.418L15.9421 18.2871L13.0186 19.9994L10.9788 19.9985L8.05356 18.2871L4.17862 19.418L3 15.5193L4.17875 11.1924L3 7.5341L4.17875 3L10.2336 6.54437H13.7639L19.8188 3L20.9976 7.5341L19.8188 11.1924L20.9976 15.5193L19.8188 19.418Z",
|
|
473
|
+
fill: "#FF5C16"
|
|
474
|
+
}
|
|
475
|
+
),
|
|
476
|
+
/* @__PURE__ */ jsx2(
|
|
477
|
+
"path",
|
|
478
|
+
{
|
|
479
|
+
d: "M4.17969 3L10.2347 6.54685L9.99394 8.98101L4.17969 3ZM8.05476 15.5209L10.7189 17.5093L8.05476 18.2869V15.5209ZM10.5059 12.2335L9.99394 8.98275L6.71642 11.1934L6.71464 11.1925V11.1941L6.72479 13.4695L8.05387 12.2336L10.5059 12.2335ZM19.819 3L13.7641 6.54685L14.004 8.98101L19.819 3ZM15.9441 15.5209L13.2798 17.5093L15.9441 18.2869V15.5209ZM17.2833 11.194V11.1924L17.2825 11.1932L14.0049 8.98275L13.4929 12.2335H15.944L17.2739 13.4693L17.2833 11.194Z",
|
|
480
|
+
fill: "#FF5C16"
|
|
481
|
+
}
|
|
482
|
+
),
|
|
483
|
+
/* @__PURE__ */ jsx2(
|
|
484
|
+
"path",
|
|
485
|
+
{
|
|
486
|
+
d: "M8.05369 18.2867L4.17875 19.4177L3 15.5207H8.05369V18.2867ZM10.5049 12.2324L11.245 16.9321L10.2191 14.319L6.72296 13.4691L8.0528 12.2325L10.5049 12.2324ZM15.9438 18.2867L19.8188 19.4177L20.9976 15.5206H15.9438C15.9438 15.5207 15.9438 18.2867 15.9438 18.2867ZM13.4927 12.2324L12.7526 16.9321L13.7783 14.319L17.2748 13.4691L15.944 12.2325L13.4927 12.2324Z",
|
|
487
|
+
fill: "#E34807"
|
|
488
|
+
}
|
|
489
|
+
),
|
|
490
|
+
/* @__PURE__ */ jsx2(
|
|
491
|
+
"path",
|
|
492
|
+
{
|
|
493
|
+
d: "M3 15.5194L4.17875 11.1924H6.71358L6.72283 13.4686L10.2194 14.3185L11.2451 16.9315L10.7178 17.5069L8.05369 15.5185H3V15.5194ZM20.9976 15.5194L19.8188 11.1924H17.2839L17.2746 13.4686L13.7783 14.3185L12.7524 16.9315L13.2796 17.5069L15.9439 15.5185H20.9976V15.5194ZM13.7639 6.54443H10.2336L9.99389 8.97859L11.2453 16.9289H12.7526L14.0047 8.97859L13.7639 6.54443Z",
|
|
494
|
+
fill: "#FF8D5D"
|
|
495
|
+
}
|
|
496
|
+
),
|
|
497
|
+
/* @__PURE__ */ jsx2(
|
|
498
|
+
"path",
|
|
499
|
+
{
|
|
500
|
+
d: "M4.17875 3L3 7.5341L4.17875 11.1924H6.71358L9.99287 8.98114L4.17875 3ZM9.77231 13.1766H8.62399L7.9988 13.7771L10.2202 14.3166L9.77231 13.1757V13.1766ZM19.8188 3L20.9976 7.5341L19.8188 11.1924H17.2839L14.0047 8.98114L19.8188 3ZM14.2269 13.1766H15.3769L16.0021 13.7778L13.7782 14.3184L14.2269 13.1757V13.1766ZM13.0178 18.4484L13.2798 17.5086L12.7524 16.9332H11.244L10.7168 17.5086L10.9787 18.4484",
|
|
501
|
+
fill: "#661800"
|
|
502
|
+
}
|
|
503
|
+
),
|
|
504
|
+
/* @__PURE__ */ jsx2(
|
|
505
|
+
"path",
|
|
506
|
+
{
|
|
507
|
+
d: "M13.0173 18.4482V20.0001H10.9785V18.4482H13.0173Z",
|
|
508
|
+
fill: "#C0C4CD"
|
|
509
|
+
}
|
|
510
|
+
),
|
|
511
|
+
/* @__PURE__ */ jsx2(
|
|
512
|
+
"path",
|
|
513
|
+
{
|
|
514
|
+
d: "M8.05469 18.2854L10.9807 19.9994V18.4475L10.7187 17.5078L8.05469 18.2854ZM15.944 18.2854L13.0179 19.9994V18.4475L13.2799 17.5078L15.944 18.2854Z",
|
|
515
|
+
fill: "#E7EBF6"
|
|
516
|
+
}
|
|
517
|
+
)
|
|
518
|
+
] }),
|
|
519
|
+
/* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsx2("clipPath", { id: "clip0_2567_1088", children: /* @__PURE__ */ jsx2(
|
|
520
|
+
"rect",
|
|
521
|
+
{
|
|
522
|
+
width: "18",
|
|
523
|
+
height: "17",
|
|
524
|
+
fill: "white",
|
|
525
|
+
transform: "translate(3 3)"
|
|
526
|
+
}
|
|
527
|
+
) }) })
|
|
528
|
+
]
|
|
529
|
+
}
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/icons/TelegramColorIcon.tsx
|
|
534
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
535
|
+
function TelegramColorIcon({ className }) {
|
|
536
|
+
return /* @__PURE__ */ jsxs2(
|
|
537
|
+
"svg",
|
|
538
|
+
{
|
|
539
|
+
width: "24",
|
|
540
|
+
height: "24",
|
|
541
|
+
viewBox: "0 0 24 24",
|
|
542
|
+
className,
|
|
543
|
+
fill: "none",
|
|
544
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
545
|
+
children: [
|
|
546
|
+
/* @__PURE__ */ jsx3(
|
|
547
|
+
"path",
|
|
548
|
+
{
|
|
549
|
+
d: "M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z",
|
|
550
|
+
fill: "url(#paint0_linear_2571_1084)"
|
|
551
|
+
}
|
|
552
|
+
),
|
|
553
|
+
/* @__PURE__ */ jsx3(
|
|
554
|
+
"path",
|
|
555
|
+
{
|
|
556
|
+
"fill-rule": "evenodd",
|
|
557
|
+
"clip-rule": "evenodd",
|
|
558
|
+
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
|
+
fill: "white"
|
|
560
|
+
}
|
|
561
|
+
),
|
|
562
|
+
/* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsxs2(
|
|
563
|
+
"linearGradient",
|
|
564
|
+
{
|
|
565
|
+
id: "paint0_linear_2571_1084",
|
|
566
|
+
x1: "903",
|
|
567
|
+
y1: "3",
|
|
568
|
+
x2: "903",
|
|
569
|
+
y2: "1789.65",
|
|
570
|
+
gradientUnits: "userSpaceOnUse",
|
|
571
|
+
children: [
|
|
572
|
+
/* @__PURE__ */ jsx3("stop", { "stop-color": "#2AABEE" }),
|
|
573
|
+
/* @__PURE__ */ jsx3("stop", { offset: "1", "stop-color": "#229ED9" })
|
|
574
|
+
]
|
|
575
|
+
}
|
|
576
|
+
) })
|
|
577
|
+
]
|
|
578
|
+
}
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// src/LoginModal.tsx
|
|
583
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
584
|
+
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";
|
|
586
|
+
var mobileQuery = typeof window !== "undefined" ? window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`) : null;
|
|
587
|
+
function subscribeMobile(cb) {
|
|
588
|
+
mobileQuery?.addEventListener("change", cb);
|
|
589
|
+
return () => mobileQuery?.removeEventListener("change", cb);
|
|
590
|
+
}
|
|
591
|
+
function getSnapshotMobile() {
|
|
592
|
+
return mobileQuery?.matches ?? false;
|
|
593
|
+
}
|
|
594
|
+
function useIsMobile() {
|
|
595
|
+
return useSyncExternalStore(subscribeMobile, getSnapshotMobile, () => false);
|
|
596
|
+
}
|
|
597
|
+
function LoginModal({ onConnectWallet, walletIcon }) {
|
|
598
|
+
const { loginTelegram, loginModalOpen, closeLoginModal, botId } = useHypurrConnect();
|
|
599
|
+
const handleTelegramAuth = useCallback2(
|
|
600
|
+
(user) => {
|
|
601
|
+
loginTelegram(user);
|
|
602
|
+
closeLoginModal();
|
|
603
|
+
},
|
|
604
|
+
[loginTelegram, closeLoginModal]
|
|
605
|
+
);
|
|
606
|
+
useEffect2(() => {
|
|
607
|
+
if (!loginModalOpen) return;
|
|
608
|
+
function onMessage(e) {
|
|
609
|
+
if (e.origin !== "https://oauth.telegram.org") return;
|
|
610
|
+
try {
|
|
611
|
+
const data = typeof e.data === "string" ? JSON.parse(e.data) : e.data;
|
|
612
|
+
if (data?.event === "auth_result" && data.result) {
|
|
613
|
+
const r = data.result;
|
|
614
|
+
handleTelegramAuth({
|
|
615
|
+
id: r.id,
|
|
616
|
+
first_name: r.first_name ?? "",
|
|
617
|
+
last_name: r.last_name ?? void 0,
|
|
618
|
+
username: r.username ?? void 0,
|
|
619
|
+
photo_url: r.photo_url ?? void 0,
|
|
620
|
+
auth_date: r.auth_date,
|
|
621
|
+
hash: r.hash
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
} catch {
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
window.addEventListener("message", onMessage);
|
|
628
|
+
return () => window.removeEventListener("message", onMessage);
|
|
629
|
+
}, [loginModalOpen, handleTelegramAuth]);
|
|
630
|
+
const openTelegramOAuth = useCallback2(() => {
|
|
631
|
+
const origin = encodeURIComponent(window.location.origin);
|
|
632
|
+
const url = `https://oauth.telegram.org/auth?bot_id=${botId}&origin=${origin}&request_access=write`;
|
|
633
|
+
const w = 550;
|
|
634
|
+
const h = 470;
|
|
635
|
+
const left = window.screenX + (window.outerWidth - w) / 2;
|
|
636
|
+
const top = window.screenY + (window.outerHeight - h) / 2;
|
|
637
|
+
window.open(
|
|
638
|
+
url,
|
|
639
|
+
"telegram_auth",
|
|
640
|
+
`width=${w},height=${h},left=${left},top=${top}`
|
|
641
|
+
);
|
|
642
|
+
}, [botId]);
|
|
643
|
+
const isMobile = useIsMobile();
|
|
644
|
+
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" }),
|
|
650
|
+
/* @__PURE__ */ jsxs3(
|
|
651
|
+
"button",
|
|
652
|
+
{
|
|
653
|
+
type: "button",
|
|
654
|
+
onClick: () => {
|
|
655
|
+
closeLoginModal();
|
|
656
|
+
onConnectWallet();
|
|
657
|
+
},
|
|
658
|
+
className: btnClass,
|
|
659
|
+
children: [
|
|
660
|
+
walletIcon ?? /* @__PURE__ */ jsx4(MetaMaskColorIcon, { className: "size-5" }),
|
|
661
|
+
"Wallet"
|
|
662
|
+
]
|
|
663
|
+
}
|
|
664
|
+
)
|
|
665
|
+
] });
|
|
666
|
+
return /* @__PURE__ */ jsx4(AnimatePresence, { children: loginModalOpen && (isMobile ? /* @__PURE__ */ jsx4(MobileDrawer, { onClose: closeLoginModal, children: modalContent }, "drawer") : /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
667
|
+
/* @__PURE__ */ jsx4(
|
|
668
|
+
motion.div,
|
|
669
|
+
{
|
|
670
|
+
className: "fixed inset-0 z-[100] bg-black/60 backdrop-blur-[2px]",
|
|
671
|
+
initial: { opacity: 0 },
|
|
672
|
+
animate: { opacity: 1 },
|
|
673
|
+
exit: { opacity: 0 },
|
|
674
|
+
transition: { duration: 0.15 },
|
|
675
|
+
onClick: closeLoginModal
|
|
676
|
+
},
|
|
677
|
+
"backdrop"
|
|
678
|
+
),
|
|
679
|
+
/* @__PURE__ */ jsx4(
|
|
680
|
+
motion.div,
|
|
681
|
+
{
|
|
682
|
+
className: "fixed inset-0 z-[101] flex items-center justify-center p-4",
|
|
683
|
+
initial: { opacity: 0 },
|
|
684
|
+
animate: { opacity: 1 },
|
|
685
|
+
exit: { opacity: 0 },
|
|
686
|
+
transition: { duration: 0.05 },
|
|
687
|
+
onClick: closeLoginModal,
|
|
688
|
+
children: /* @__PURE__ */ jsxs3(
|
|
689
|
+
motion.div,
|
|
690
|
+
{
|
|
691
|
+
className: "flex w-[400px] flex-col items-center gap-4 overflow-hidden rounded-xl border border-white/10 bg-[#282828] p-6",
|
|
692
|
+
initial: { opacity: 0, scale: 0.95, y: 10 },
|
|
693
|
+
animate: { opacity: 1, scale: 1, y: 0 },
|
|
694
|
+
exit: { opacity: 0, scale: 0.95, y: 10 },
|
|
695
|
+
transition: { duration: 0.2, ease: "easeOut" },
|
|
696
|
+
onClick: (e) => e.stopPropagation(),
|
|
697
|
+
children: [
|
|
698
|
+
/* @__PURE__ */ jsx4("p", { className: "text-base font-bold tracking-tight text-white", children: "Connect" }),
|
|
699
|
+
modalContent
|
|
700
|
+
]
|
|
701
|
+
}
|
|
702
|
+
)
|
|
703
|
+
},
|
|
704
|
+
"modal-wrapper"
|
|
705
|
+
)
|
|
706
|
+
] })) });
|
|
707
|
+
}
|
|
708
|
+
function MobileDrawer({
|
|
709
|
+
children,
|
|
710
|
+
onClose
|
|
711
|
+
}) {
|
|
712
|
+
const controls = useAnimationControls();
|
|
713
|
+
const handleDragEnd = useCallback2(
|
|
714
|
+
(_, info) => {
|
|
715
|
+
if (info.offset.y > 100 || info.velocity.y > 500) {
|
|
716
|
+
onClose();
|
|
717
|
+
} else {
|
|
718
|
+
controls.start({ y: 0 });
|
|
719
|
+
}
|
|
720
|
+
},
|
|
721
|
+
[onClose, controls]
|
|
722
|
+
);
|
|
723
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
724
|
+
/* @__PURE__ */ jsx4(
|
|
725
|
+
motion.div,
|
|
726
|
+
{
|
|
727
|
+
className: "fixed inset-0 z-[100] bg-black/60 backdrop-blur-[2px]",
|
|
728
|
+
initial: { opacity: 0 },
|
|
729
|
+
animate: { opacity: 1 },
|
|
730
|
+
exit: { opacity: 0 },
|
|
731
|
+
transition: { duration: 0.15 },
|
|
732
|
+
onClick: onClose
|
|
733
|
+
},
|
|
734
|
+
"drawer-backdrop"
|
|
735
|
+
),
|
|
736
|
+
/* @__PURE__ */ jsxs3(
|
|
737
|
+
motion.div,
|
|
738
|
+
{
|
|
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",
|
|
740
|
+
initial: { y: "100%" },
|
|
741
|
+
animate: { y: 0 },
|
|
742
|
+
exit: { y: "100%" },
|
|
743
|
+
transition: { type: "tween", duration: 0.3, ease: [0.32, 0.72, 0, 1] },
|
|
744
|
+
drag: "y",
|
|
745
|
+
dragConstraints: { top: 0, bottom: 0 },
|
|
746
|
+
dragElastic: { top: 0, bottom: 0.4 },
|
|
747
|
+
onDragEnd: handleDragEnd,
|
|
748
|
+
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" }),
|
|
752
|
+
children
|
|
753
|
+
]
|
|
754
|
+
},
|
|
755
|
+
"drawer-sheet"
|
|
756
|
+
)
|
|
757
|
+
] });
|
|
758
|
+
}
|
|
759
|
+
export {
|
|
760
|
+
GrpcExchangeTransport,
|
|
761
|
+
HypurrConnectProvider,
|
|
762
|
+
LoginModal,
|
|
763
|
+
createStaticClient,
|
|
764
|
+
createTelegramClient,
|
|
765
|
+
useHypurrConnect
|
|
766
|
+
};
|
|
767
|
+
//# sourceMappingURL=index.js.map
|