@hfunlabs/hypurr-connect 0.1.1 → 0.1.3
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 +294 -79
- package/dist/index.d.ts +77 -9
- package/dist/index.js +545 -164
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/HypurrConnectProvider.tsx +557 -164
- package/src/LoginModal.tsx +21 -7
- package/src/TelegramLoginWidget.tsx +62 -0
- package/src/agent.ts +80 -0
- package/src/index.ts +8 -0
- package/src/types.ts +89 -11
package/dist/index.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
// src/HypurrConnectProvider.tsx
|
|
2
2
|
import {
|
|
3
3
|
ExchangeClient,
|
|
4
|
-
HttpTransport
|
|
5
|
-
InfoClient
|
|
4
|
+
HttpTransport
|
|
6
5
|
} from "@hfunlabs/hyperliquid";
|
|
7
|
-
import { PrivateKeySigner } from "@hfunlabs/hyperliquid/signing";
|
|
6
|
+
import { PrivateKeySigner, signUserSignedAction } from "@hfunlabs/hyperliquid/signing";
|
|
8
7
|
import {
|
|
9
8
|
createContext,
|
|
10
9
|
useCallback,
|
|
11
10
|
useContext,
|
|
12
11
|
useEffect,
|
|
13
12
|
useMemo,
|
|
13
|
+
useRef,
|
|
14
14
|
useState
|
|
15
15
|
} from "react";
|
|
16
16
|
|
|
17
17
|
// src/agent.ts
|
|
18
|
+
var AGENT_NAME = "hypurr-connect";
|
|
18
19
|
var AGENT_STORAGE_PREFIX = "hypurr-connect-agent";
|
|
19
20
|
function storageKey(masterAddress) {
|
|
20
21
|
return `${AGENT_STORAGE_PREFIX}:${masterAddress.toLowerCase()}`;
|
|
@@ -41,6 +42,39 @@ async function generateAgentKey() {
|
|
|
41
42
|
const signer = new PrivateKeySigner2(privateKey);
|
|
42
43
|
return { privateKey, address: signer.address };
|
|
43
44
|
}
|
|
45
|
+
async function fetchActiveAgent(userAddress, isTestnet) {
|
|
46
|
+
const url = isTestnet ? "https://api.hyperliquid-testnet.xyz/info" : "https://api.hyperliquid.xyz/info";
|
|
47
|
+
const res = await fetch(url, {
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: { "Content-Type": "application/json" },
|
|
50
|
+
body: JSON.stringify({ type: "extraAgents", user: userAddress })
|
|
51
|
+
});
|
|
52
|
+
if (!res.ok) return null;
|
|
53
|
+
const agents = await res.json();
|
|
54
|
+
if (!Array.isArray(agents)) return null;
|
|
55
|
+
const nowMs = Date.now();
|
|
56
|
+
const match = agents.find(
|
|
57
|
+
(a) => a.name === AGENT_NAME && a.validUntil * 1e3 > nowMs
|
|
58
|
+
);
|
|
59
|
+
if (!match) return null;
|
|
60
|
+
return { ...match, validUntil: match.validUntil * 1e3 };
|
|
61
|
+
}
|
|
62
|
+
async function isAgentValid(stored, userAddress, isTestnet) {
|
|
63
|
+
if (stored.validUntil <= Date.now()) return false;
|
|
64
|
+
const remote = await fetchActiveAgent(userAddress, isTestnet);
|
|
65
|
+
if (!remote) return false;
|
|
66
|
+
return remote.address.toLowerCase() === stored.address.toLowerCase() && remote.validUntil > Date.now();
|
|
67
|
+
}
|
|
68
|
+
var DEAD_AGENT_PATTERNS = [
|
|
69
|
+
/agent address .+ is not valid/i,
|
|
70
|
+
/unknown signer/i,
|
|
71
|
+
/not authorized/i,
|
|
72
|
+
/not an agent/i
|
|
73
|
+
];
|
|
74
|
+
function isDeadAgentError(err) {
|
|
75
|
+
const msg = err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : String(err);
|
|
76
|
+
return DEAD_AGENT_PATTERNS.some((p) => p.test(msg));
|
|
77
|
+
}
|
|
44
78
|
|
|
45
79
|
// src/grpc.ts
|
|
46
80
|
import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
|
|
@@ -152,6 +186,14 @@ function useHypurrConnect() {
|
|
|
152
186
|
);
|
|
153
187
|
return ctx;
|
|
154
188
|
}
|
|
189
|
+
function useHypurrConnectInternal() {
|
|
190
|
+
const ctx = useContext(HypurrConnectContext);
|
|
191
|
+
if (!ctx)
|
|
192
|
+
throw new Error(
|
|
193
|
+
"useHypurrConnectInternal must be used within <HypurrConnectProvider>"
|
|
194
|
+
);
|
|
195
|
+
return ctx;
|
|
196
|
+
}
|
|
155
197
|
function HypurrConnectProvider({
|
|
156
198
|
config,
|
|
157
199
|
children
|
|
@@ -175,6 +217,7 @@ function HypurrConnectProvider({
|
|
|
175
217
|
() => tgLoginData ? toAuthDataMap(tgLoginData) : {},
|
|
176
218
|
[tgLoginData]
|
|
177
219
|
);
|
|
220
|
+
const [tgUserTick, setTgUserTick] = useState(0);
|
|
178
221
|
useEffect(() => {
|
|
179
222
|
if (!tgLoginData) return;
|
|
180
223
|
let cancelled = false;
|
|
@@ -183,9 +226,7 @@ function HypurrConnectProvider({
|
|
|
183
226
|
(async () => {
|
|
184
227
|
try {
|
|
185
228
|
const authData = toAuthDataMap(tgLoginData);
|
|
186
|
-
console.log(authData);
|
|
187
229
|
const { response } = await tgClient.telegramUser({ authData });
|
|
188
|
-
console.log(response);
|
|
189
230
|
if (cancelled) return;
|
|
190
231
|
setTgUser(response.user ?? null);
|
|
191
232
|
} catch (err) {
|
|
@@ -199,27 +240,56 @@ function HypurrConnectProvider({
|
|
|
199
240
|
return () => {
|
|
200
241
|
cancelled = true;
|
|
201
242
|
};
|
|
202
|
-
}, [tgLoginData, tgClient]);
|
|
243
|
+
}, [tgLoginData, tgClient, tgUserTick]);
|
|
203
244
|
const [eoaAddress, setEoaAddress] = useState(null);
|
|
204
245
|
const [agent, setAgent] = useState(null);
|
|
246
|
+
const [eoaLoading, setEoaLoading] = useState(false);
|
|
247
|
+
const [eoaError, setEoaError] = useState(null);
|
|
248
|
+
const eoaSignerRef = useRef(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), []);
|
|
205
254
|
useEffect(() => {
|
|
206
|
-
if (
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
255
|
+
if (authMethod !== "telegram" || !tgUser) {
|
|
256
|
+
setWallets([]);
|
|
257
|
+
setSelectedWalletId(0);
|
|
258
|
+
setPacks([]);
|
|
259
|
+
return;
|
|
210
260
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
+
);
|
|
214
282
|
const user = useMemo(() => {
|
|
215
|
-
if (tgLoginData && authMethod === "telegram") {
|
|
283
|
+
if (tgLoginData && authMethod === "telegram" && selectedWallet) {
|
|
216
284
|
return {
|
|
217
|
-
address:
|
|
218
|
-
walletId:
|
|
285
|
+
address: selectedWallet.ethereumAddress,
|
|
286
|
+
walletId: selectedWallet.id,
|
|
219
287
|
displayName: tgLoginData.username ? `@${tgLoginData.username}` : tgLoginData.first_name,
|
|
220
288
|
photoUrl: tgLoginData.photo_url,
|
|
221
289
|
authMethod: "telegram",
|
|
222
|
-
telegramId: String(tgLoginData.id)
|
|
290
|
+
telegramId: String(tgLoginData.id),
|
|
291
|
+
hfunScore: tgUser?.reputation?.hfunScore,
|
|
292
|
+
reputationScore: tgUser?.reputation?.reputationScore
|
|
223
293
|
};
|
|
224
294
|
}
|
|
225
295
|
if (eoaAddress && authMethod === "eoa") {
|
|
@@ -231,7 +301,23 @@ function HypurrConnectProvider({
|
|
|
231
301
|
};
|
|
232
302
|
}
|
|
233
303
|
return null;
|
|
234
|
-
}, [tgLoginData,
|
|
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 agentSignerRef = useRef(
|
|
314
|
+
agent ? new PrivateKeySigner(agent.privateKey) : null
|
|
315
|
+
);
|
|
316
|
+
useEffect(() => {
|
|
317
|
+
agentSignerRef.current = agent ? new PrivateKeySigner(agent.privateKey) : null;
|
|
318
|
+
}, [agent]);
|
|
319
|
+
const provisioningRef = useRef(null);
|
|
320
|
+
const agentReady = authMethod === "telegram" || authMethod === "eoa" && !!agent;
|
|
235
321
|
const exchange = useMemo(() => {
|
|
236
322
|
if (authMethod === "telegram" && user?.address) {
|
|
237
323
|
const transport = new GrpcExchangeTransport({
|
|
@@ -246,127 +332,229 @@ function HypurrConnectProvider({
|
|
|
246
332
|
userAddress: user.address
|
|
247
333
|
});
|
|
248
334
|
}
|
|
249
|
-
if (authMethod === "eoa" &&
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
isTestnet: config.isTestnet ?? false
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
})
|
|
265
|
-
}),
|
|
266
|
-
[config.isTestnet]
|
|
267
|
-
);
|
|
268
|
-
const [usdcBalance, setUsdcBalance] = useState(null);
|
|
269
|
-
const [usdcBalanceLoading, setUsdcBalanceLoading] = useState(false);
|
|
270
|
-
const [balanceTick, setBalanceTick] = useState(0);
|
|
271
|
-
const refreshBalance = useCallback(() => setBalanceTick((t) => t + 1), []);
|
|
272
|
-
useEffect(() => {
|
|
273
|
-
const addr = user?.address;
|
|
274
|
-
if (!addr) {
|
|
275
|
-
setUsdcBalance(null);
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
let cancelled = false;
|
|
279
|
-
setUsdcBalanceLoading(true);
|
|
280
|
-
(async () => {
|
|
281
|
-
try {
|
|
282
|
-
const state = await infoClient.clearinghouseState({
|
|
283
|
-
user: addr
|
|
335
|
+
if (authMethod === "eoa" && eoaAddress) {
|
|
336
|
+
const hasSigner = !!eoaSignerRef.current;
|
|
337
|
+
if (!agent && !hasSigner) {
|
|
338
|
+
const noAgentTransport = {
|
|
339
|
+
isTestnet: config.isTestnet ?? false,
|
|
340
|
+
request() {
|
|
341
|
+
throw new Error(
|
|
342
|
+
"[HypurrConnect] No agent key approved and no wallet signer available. Either call approveAgent(signTypedDataAsync) or pass a signer to connectEoa(address, { signTypedData, chainId })."
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
return new ExchangeClient({
|
|
347
|
+
transport: noAgentTransport,
|
|
348
|
+
externalSigning: true,
|
|
349
|
+
userAddress: eoaAddress
|
|
284
350
|
});
|
|
285
|
-
if (!cancelled) {
|
|
286
|
-
setUsdcBalance(state.withdrawable);
|
|
287
|
-
}
|
|
288
|
-
} catch (err) {
|
|
289
|
-
console.error("[HypurrConnect] Failed to fetch USDC balance:", err);
|
|
290
|
-
if (!cancelled) setUsdcBalance(null);
|
|
291
|
-
} finally {
|
|
292
|
-
if (!cancelled) setUsdcBalanceLoading(false);
|
|
293
351
|
}
|
|
294
|
-
})();
|
|
295
|
-
return () => {
|
|
296
|
-
cancelled = true;
|
|
297
|
-
};
|
|
298
|
-
}, [user?.address, infoClient, balanceTick]);
|
|
299
|
-
const approveAgent = useCallback(
|
|
300
|
-
async (signTypedDataAsync) => {
|
|
301
|
-
if (!eoaAddress) throw new Error("No EOA address connected");
|
|
302
|
-
const { privateKey, address: agentAddress } = await generateAgentKey();
|
|
303
352
|
const isTestnet = config.isTestnet ?? false;
|
|
304
|
-
const
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
353
|
+
const inner = new HttpTransport({ isTestnet });
|
|
354
|
+
const deadAgentAddr = eoaAddress;
|
|
355
|
+
const guardedTransport = {
|
|
356
|
+
isTestnet: inner.isTestnet,
|
|
357
|
+
async request(endpoint, payload, signal) {
|
|
358
|
+
try {
|
|
359
|
+
return await inner.request(endpoint, payload, signal);
|
|
360
|
+
} catch (err) {
|
|
361
|
+
if (endpoint === "exchange" && isDeadAgentError(err)) {
|
|
362
|
+
onDeadAgentRef.current?.(deadAgentAddr);
|
|
363
|
+
}
|
|
364
|
+
throw err;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
312
367
|
};
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
368
|
+
const signerRef = eoaSignerRef;
|
|
369
|
+
const agentRef = agentSignerRef;
|
|
370
|
+
const provRef = provisioningRef;
|
|
371
|
+
const ownerAddress = eoaAddress;
|
|
372
|
+
const ensureAgent = async () => {
|
|
373
|
+
const existing = agentRef.current;
|
|
374
|
+
if (existing) return existing;
|
|
375
|
+
if (provRef.current) return provRef.current;
|
|
376
|
+
const signer = signerRef.current;
|
|
377
|
+
if (!signer) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
"[HypurrConnect] No wallet signer available to auto-provision agent. Pass a signer to connectEoa(address, { signTypedData, chainId })."
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
provRef.current = (async () => {
|
|
383
|
+
try {
|
|
384
|
+
const { privateKey, address: agentAddress } = await generateAgentKey();
|
|
385
|
+
const chainIdHex = `0x${signer.chainId.toString(16)}`;
|
|
386
|
+
const nonce = Date.now();
|
|
387
|
+
const action = {
|
|
388
|
+
type: "approveAgent",
|
|
389
|
+
signatureChainId: chainIdHex,
|
|
390
|
+
hyperliquidChain: isTestnet ? "Testnet" : "Mainnet",
|
|
391
|
+
agentAddress: agentAddress.toLowerCase(),
|
|
392
|
+
agentName: AGENT_NAME,
|
|
393
|
+
nonce
|
|
394
|
+
};
|
|
395
|
+
const approveAgentTypes = {
|
|
396
|
+
"HyperliquidTransaction:ApproveAgent": [
|
|
397
|
+
{ name: "hyperliquidChain", type: "string" },
|
|
398
|
+
{ name: "agentAddress", type: "address" },
|
|
399
|
+
{ name: "agentName", type: "string" },
|
|
400
|
+
{ name: "nonce", type: "uint64" }
|
|
401
|
+
]
|
|
402
|
+
};
|
|
403
|
+
const wallet = {
|
|
404
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
405
|
+
signTypedData(params) {
|
|
406
|
+
return signer.signTypedData(params);
|
|
407
|
+
},
|
|
408
|
+
getAddresses: async () => [ownerAddress],
|
|
409
|
+
getChainId: async () => signer.chainId
|
|
410
|
+
};
|
|
411
|
+
const signature = await signUserSignedAction({
|
|
412
|
+
wallet,
|
|
413
|
+
action,
|
|
414
|
+
types: approveAgentTypes
|
|
415
|
+
});
|
|
416
|
+
const apiUrl = isTestnet ? "https://api.hyperliquid-testnet.xyz/exchange" : "https://api.hyperliquid.xyz/exchange";
|
|
417
|
+
const res = await fetch(apiUrl, {
|
|
418
|
+
method: "POST",
|
|
419
|
+
headers: { "Content-Type": "application/json" },
|
|
420
|
+
body: JSON.stringify({ action, signature, nonce })
|
|
421
|
+
});
|
|
422
|
+
const body = await res.json();
|
|
423
|
+
if (body?.status === "err") {
|
|
424
|
+
throw new Error(
|
|
425
|
+
`approveAgent API error: ${body.response ?? JSON.stringify(body)}`
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
const remote = await fetchActiveAgent(ownerAddress, isTestnet);
|
|
429
|
+
const validUntil = remote?.validUntil ?? Date.now() + 7 * 24 * 60 * 60 * 1e3;
|
|
430
|
+
const stored = {
|
|
431
|
+
privateKey,
|
|
432
|
+
address: agentAddress,
|
|
433
|
+
approvedAt: Date.now(),
|
|
434
|
+
validUntil
|
|
435
|
+
};
|
|
436
|
+
saveAgent(ownerAddress, stored);
|
|
437
|
+
const newSigner = new PrivateKeySigner(privateKey);
|
|
438
|
+
agentRef.current = newSigner;
|
|
439
|
+
setAgent(stored);
|
|
440
|
+
return newSigner;
|
|
441
|
+
} finally {
|
|
442
|
+
provRef.current = null;
|
|
443
|
+
}
|
|
444
|
+
})();
|
|
445
|
+
return provRef.current;
|
|
320
446
|
};
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
447
|
+
const dualWallet = {
|
|
448
|
+
address: ownerAddress,
|
|
449
|
+
async signTypedData(params) {
|
|
450
|
+
if (params.domain.name === "HyperliquidSignTransaction") {
|
|
451
|
+
const signer = signerRef.current;
|
|
452
|
+
if (!signer) {
|
|
453
|
+
throw new Error(
|
|
454
|
+
"[HypurrConnect] No wallet signer available for user-signed actions. Pass a signer to connectEoa(address, { signTypedData, chainId })."
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
return signer.signTypedData(
|
|
458
|
+
params
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
const agentSigner = await ensureAgent();
|
|
462
|
+
return agentSigner.signTypedData(params);
|
|
335
463
|
}
|
|
336
|
-
});
|
|
337
|
-
const r = `0x${signature.slice(2, 66)}`;
|
|
338
|
-
const s = `0x${signature.slice(66, 130)}`;
|
|
339
|
-
const v = parseInt(signature.slice(130, 132), 16);
|
|
340
|
-
const url = isTestnet ? "https://api.hyperliquid-testnet.xyz/exchange" : "https://api.hyperliquid.xyz/exchange";
|
|
341
|
-
const res = await fetch(url, {
|
|
342
|
-
method: "POST",
|
|
343
|
-
headers: { "Content-Type": "application/json" },
|
|
344
|
-
body: JSON.stringify({
|
|
345
|
-
action,
|
|
346
|
-
nonce,
|
|
347
|
-
signature: { r, s, v }
|
|
348
|
-
})
|
|
349
|
-
});
|
|
350
|
-
const body = await res.json();
|
|
351
|
-
if (body?.status !== "ok") {
|
|
352
|
-
throw new Error(`approveAgent failed: ${JSON.stringify(body)}`);
|
|
353
|
-
}
|
|
354
|
-
const stored = {
|
|
355
|
-
privateKey,
|
|
356
|
-
address: agentAddress,
|
|
357
|
-
approvedAt: Date.now()
|
|
358
464
|
};
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
465
|
+
return new ExchangeClient({
|
|
466
|
+
transport: guardedTransport,
|
|
467
|
+
wallet: dualWallet,
|
|
468
|
+
signatureChainId: () => {
|
|
469
|
+
const id = signerRef.current?.chainId ?? 42161;
|
|
470
|
+
return `0x${id.toString(16)}`;
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
return null;
|
|
475
|
+
}, [
|
|
476
|
+
authMethod,
|
|
477
|
+
user,
|
|
478
|
+
agent,
|
|
479
|
+
eoaAddress,
|
|
480
|
+
config.isTestnet,
|
|
481
|
+
tgClient,
|
|
482
|
+
authDataMap
|
|
483
|
+
]);
|
|
364
484
|
const handleClearAgent = useCallback(() => {
|
|
365
485
|
if (eoaAddress) {
|
|
366
486
|
clearAgent(eoaAddress);
|
|
367
487
|
setAgent(null);
|
|
368
488
|
}
|
|
369
489
|
}, [eoaAddress]);
|
|
490
|
+
const createWallet = useCallback(
|
|
491
|
+
async (name) => {
|
|
492
|
+
const { response } = await tgClient.hyperliquidWalletCreate({
|
|
493
|
+
authData: authDataMap,
|
|
494
|
+
name
|
|
495
|
+
});
|
|
496
|
+
refreshWallets();
|
|
497
|
+
if (!response.wallet)
|
|
498
|
+
throw new Error("Wallet creation returned no wallet");
|
|
499
|
+
return response.wallet;
|
|
500
|
+
},
|
|
501
|
+
[tgClient, authDataMap, refreshWallets]
|
|
502
|
+
);
|
|
503
|
+
const deleteWallet = useCallback(
|
|
504
|
+
async (walletId) => {
|
|
505
|
+
await tgClient.hyperliquidWalletDelete({
|
|
506
|
+
authData: authDataMap,
|
|
507
|
+
walletId
|
|
508
|
+
});
|
|
509
|
+
if (walletId === selectedWalletId) {
|
|
510
|
+
const remaining = wallets.filter((w) => w.id !== walletId);
|
|
511
|
+
setSelectedWalletId(remaining[0]?.id ?? 0);
|
|
512
|
+
}
|
|
513
|
+
refreshWallets();
|
|
514
|
+
},
|
|
515
|
+
[tgClient, authDataMap, selectedWalletId, wallets, refreshWallets]
|
|
516
|
+
);
|
|
517
|
+
const createWalletPack = useCallback(
|
|
518
|
+
async (name) => {
|
|
519
|
+
const { response } = await tgClient.telegramChatWalletPackCreate({
|
|
520
|
+
authData: authDataMap,
|
|
521
|
+
name
|
|
522
|
+
});
|
|
523
|
+
refreshWallets();
|
|
524
|
+
return response.packId;
|
|
525
|
+
},
|
|
526
|
+
[tgClient, authDataMap, refreshWallets]
|
|
527
|
+
);
|
|
528
|
+
const addPackLabel = useCallback(
|
|
529
|
+
async (params) => {
|
|
530
|
+
await tgClient.telegramChatWalletPackLabelAdd({
|
|
531
|
+
authData: authDataMap,
|
|
532
|
+
...params
|
|
533
|
+
});
|
|
534
|
+
refreshWallets();
|
|
535
|
+
},
|
|
536
|
+
[tgClient, authDataMap, refreshWallets]
|
|
537
|
+
);
|
|
538
|
+
const modifyPackLabel = useCallback(
|
|
539
|
+
async (params) => {
|
|
540
|
+
await tgClient.telegramChatWalletPackLabelModify({
|
|
541
|
+
authData: authDataMap,
|
|
542
|
+
...params
|
|
543
|
+
});
|
|
544
|
+
refreshWallets();
|
|
545
|
+
},
|
|
546
|
+
[tgClient, authDataMap, refreshWallets]
|
|
547
|
+
);
|
|
548
|
+
const removePackLabel = useCallback(
|
|
549
|
+
async (params) => {
|
|
550
|
+
await tgClient.telegramChatWalletPackLabelRemove({
|
|
551
|
+
authData: authDataMap,
|
|
552
|
+
...params
|
|
553
|
+
});
|
|
554
|
+
refreshWallets();
|
|
555
|
+
},
|
|
556
|
+
[tgClient, authDataMap, refreshWallets]
|
|
557
|
+
);
|
|
370
558
|
const [loginModalOpen, setLoginModalOpen] = useState(false);
|
|
371
559
|
const openLoginModal = useCallback(() => setLoginModalOpen(true), []);
|
|
372
560
|
const closeLoginModal = useCallback(() => setLoginModalOpen(false), []);
|
|
@@ -375,44 +563,155 @@ function HypurrConnectProvider({
|
|
|
375
563
|
localStorage.setItem(TELEGRAM_STORAGE_KEY, JSON.stringify(data));
|
|
376
564
|
setEoaAddress(null);
|
|
377
565
|
setAgent(null);
|
|
566
|
+
setEoaError(null);
|
|
378
567
|
}, []);
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
568
|
+
const connectEoa = useCallback(
|
|
569
|
+
(address, signer) => {
|
|
570
|
+
eoaSignerRef.current = signer ?? null;
|
|
571
|
+
setEoaAddress(address);
|
|
572
|
+
setTgLoginData(null);
|
|
573
|
+
setTgUser(null);
|
|
574
|
+
setTgError(null);
|
|
575
|
+
setEoaError(null);
|
|
576
|
+
localStorage.removeItem(TELEGRAM_STORAGE_KEY);
|
|
577
|
+
const existing = loadAgent(address);
|
|
578
|
+
if (existing && existing.validUntil > Date.now()) {
|
|
579
|
+
setAgent(existing);
|
|
580
|
+
} else {
|
|
581
|
+
if (existing) clearAgent(address);
|
|
582
|
+
setAgent(null);
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
[]
|
|
586
|
+
);
|
|
587
|
+
const approveAgentFn = useCallback(
|
|
588
|
+
async (signTypedDataAsync, chainId) => {
|
|
589
|
+
if (!eoaAddress) {
|
|
590
|
+
throw new Error(
|
|
591
|
+
"[HypurrConnect] Cannot approve agent: no EOA wallet connected. Call connectEoa(address) first."
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
eoaSignerRef.current = { signTypedData: signTypedDataAsync, chainId };
|
|
595
|
+
setEoaLoading(true);
|
|
596
|
+
setEoaError(null);
|
|
597
|
+
try {
|
|
598
|
+
const existing = loadAgent(eoaAddress);
|
|
599
|
+
if (existing) {
|
|
600
|
+
const isTestnet2 = config.isTestnet ?? false;
|
|
601
|
+
const valid = await isAgentValid(existing, eoaAddress, isTestnet2);
|
|
602
|
+
if (valid) {
|
|
603
|
+
setAgent(existing);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
clearAgent(eoaAddress);
|
|
607
|
+
}
|
|
608
|
+
const { privateKey, address: agentAddress } = await generateAgentKey();
|
|
609
|
+
const isTestnet = config.isTestnet ?? false;
|
|
610
|
+
const chainIdHex = `0x${chainId.toString(16)}`;
|
|
611
|
+
const nonce = Date.now();
|
|
612
|
+
const action = {
|
|
613
|
+
type: "approveAgent",
|
|
614
|
+
signatureChainId: chainIdHex,
|
|
615
|
+
hyperliquidChain: isTestnet ? "Testnet" : "Mainnet",
|
|
616
|
+
agentAddress: agentAddress.toLowerCase(),
|
|
617
|
+
agentName: AGENT_NAME,
|
|
618
|
+
nonce
|
|
619
|
+
};
|
|
620
|
+
const approveAgentTypes = {
|
|
621
|
+
"HyperliquidTransaction:ApproveAgent": [
|
|
622
|
+
{ name: "hyperliquidChain", type: "string" },
|
|
623
|
+
{ name: "agentAddress", type: "address" },
|
|
624
|
+
{ name: "agentName", type: "string" },
|
|
625
|
+
{ name: "nonce", type: "uint64" }
|
|
626
|
+
]
|
|
627
|
+
};
|
|
628
|
+
const wallet = {
|
|
629
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
630
|
+
signTypedData(params) {
|
|
631
|
+
return signTypedDataAsync(params);
|
|
632
|
+
},
|
|
633
|
+
getAddresses: async () => [eoaAddress],
|
|
634
|
+
getChainId: async () => chainId
|
|
635
|
+
};
|
|
636
|
+
const signature = await signUserSignedAction({
|
|
637
|
+
wallet,
|
|
638
|
+
action,
|
|
639
|
+
types: approveAgentTypes
|
|
640
|
+
});
|
|
641
|
+
const apiUrl = isTestnet ? "https://api.hyperliquid-testnet.xyz/exchange" : "https://api.hyperliquid.xyz/exchange";
|
|
642
|
+
const res = await fetch(apiUrl, {
|
|
643
|
+
method: "POST",
|
|
644
|
+
headers: { "Content-Type": "application/json" },
|
|
645
|
+
body: JSON.stringify({ action, signature, nonce })
|
|
646
|
+
});
|
|
647
|
+
const body = await res.json();
|
|
648
|
+
if (body?.status === "err") {
|
|
649
|
+
throw new Error(
|
|
650
|
+
`approveAgent API error: ${body.response ?? JSON.stringify(body)}`
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
const remote = await fetchActiveAgent(eoaAddress, isTestnet);
|
|
654
|
+
const validUntil = remote?.validUntil ?? Date.now() + 7 * 24 * 60 * 60 * 1e3;
|
|
655
|
+
const stored = {
|
|
656
|
+
privateKey,
|
|
657
|
+
address: agentAddress,
|
|
658
|
+
approvedAt: Date.now(),
|
|
659
|
+
validUntil
|
|
660
|
+
};
|
|
661
|
+
saveAgent(eoaAddress, stored);
|
|
662
|
+
setAgent(stored);
|
|
663
|
+
} catch (err) {
|
|
664
|
+
console.error("[HypurrConnect] EOA agent approval failed:", err);
|
|
665
|
+
setEoaError(err instanceof Error ? err.message : String(err));
|
|
666
|
+
setAgent(null);
|
|
667
|
+
} finally {
|
|
668
|
+
setEoaLoading(false);
|
|
669
|
+
}
|
|
670
|
+
},
|
|
671
|
+
[eoaAddress, config.isTestnet]
|
|
672
|
+
);
|
|
386
673
|
const logout = useCallback(() => {
|
|
387
674
|
setTgLoginData(null);
|
|
388
675
|
setTgUser(null);
|
|
389
676
|
setTgError(null);
|
|
390
677
|
setEoaAddress(null);
|
|
391
678
|
setAgent(null);
|
|
679
|
+
setEoaError(null);
|
|
680
|
+
eoaSignerRef.current = null;
|
|
392
681
|
localStorage.removeItem(TELEGRAM_STORAGE_KEY);
|
|
393
682
|
}, []);
|
|
394
683
|
const value = useMemo(
|
|
395
684
|
() => ({
|
|
396
685
|
user,
|
|
397
686
|
isLoggedIn: !!user,
|
|
398
|
-
isLoading: tgLoading,
|
|
399
|
-
error: tgError,
|
|
687
|
+
isLoading: tgLoading || eoaLoading,
|
|
688
|
+
error: tgError ?? eoaError,
|
|
400
689
|
authMethod,
|
|
401
690
|
exchange,
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
691
|
+
wallets,
|
|
692
|
+
selectedWalletId,
|
|
693
|
+
selectWallet,
|
|
694
|
+
createWallet,
|
|
695
|
+
deleteWallet,
|
|
696
|
+
refreshWallets,
|
|
697
|
+
packs,
|
|
698
|
+
createWalletPack,
|
|
699
|
+
addPackLabel,
|
|
700
|
+
modifyPackLabel,
|
|
701
|
+
removePackLabel,
|
|
405
702
|
loginModalOpen,
|
|
406
703
|
openLoginModal,
|
|
407
704
|
closeLoginModal,
|
|
408
705
|
loginTelegram,
|
|
409
|
-
|
|
706
|
+
connectEoa,
|
|
707
|
+
approveAgent: approveAgentFn,
|
|
410
708
|
logout,
|
|
411
709
|
agent,
|
|
412
|
-
agentReady
|
|
413
|
-
approveAgent,
|
|
710
|
+
agentReady,
|
|
414
711
|
clearAgent: handleClearAgent,
|
|
415
712
|
botId: config.telegram?.botId ?? "",
|
|
713
|
+
botUsername: config.telegram?.botUsername ?? "",
|
|
714
|
+
useWidget: config.telegram?.useWidget ?? false,
|
|
416
715
|
authDataMap,
|
|
417
716
|
telegramClient: tgClient,
|
|
418
717
|
staticClient
|
|
@@ -420,22 +719,35 @@ function HypurrConnectProvider({
|
|
|
420
719
|
[
|
|
421
720
|
user,
|
|
422
721
|
tgLoading,
|
|
722
|
+
eoaLoading,
|
|
423
723
|
tgError,
|
|
724
|
+
eoaError,
|
|
424
725
|
authMethod,
|
|
425
726
|
exchange,
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
727
|
+
wallets,
|
|
728
|
+
selectedWalletId,
|
|
729
|
+
selectWallet,
|
|
730
|
+
createWallet,
|
|
731
|
+
deleteWallet,
|
|
732
|
+
refreshWallets,
|
|
733
|
+
packs,
|
|
734
|
+
createWalletPack,
|
|
735
|
+
addPackLabel,
|
|
736
|
+
modifyPackLabel,
|
|
737
|
+
removePackLabel,
|
|
429
738
|
loginModalOpen,
|
|
430
739
|
openLoginModal,
|
|
431
740
|
closeLoginModal,
|
|
432
741
|
loginTelegram,
|
|
433
|
-
|
|
742
|
+
connectEoa,
|
|
743
|
+
approveAgentFn,
|
|
434
744
|
logout,
|
|
435
745
|
agent,
|
|
436
|
-
|
|
746
|
+
agentReady,
|
|
437
747
|
handleClearAgent,
|
|
438
748
|
config.telegram?.botId,
|
|
749
|
+
config.telegram?.botUsername,
|
|
750
|
+
config.telegram?.useWidget,
|
|
439
751
|
authDataMap,
|
|
440
752
|
tgClient,
|
|
441
753
|
staticClient
|
|
@@ -452,7 +764,7 @@ import {
|
|
|
452
764
|
} from "framer-motion";
|
|
453
765
|
import {
|
|
454
766
|
useCallback as useCallback2,
|
|
455
|
-
useEffect as
|
|
767
|
+
useEffect as useEffect3,
|
|
456
768
|
useSyncExternalStore
|
|
457
769
|
} from "react";
|
|
458
770
|
|
|
@@ -583,8 +895,53 @@ function TelegramColorIcon({ style }) {
|
|
|
583
895
|
);
|
|
584
896
|
}
|
|
585
897
|
|
|
898
|
+
// src/TelegramLoginWidget.tsx
|
|
899
|
+
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
900
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
901
|
+
var WIDGET_SCRIPT_URL = "https://telegram.org/js/telegram-widget.js?22";
|
|
902
|
+
var CALLBACK_NAME = "__hypurrConnectTelegramAuth";
|
|
903
|
+
function TelegramLoginWidget({
|
|
904
|
+
botUsername,
|
|
905
|
+
onAuth,
|
|
906
|
+
buttonSize = "large",
|
|
907
|
+
cornerRadius,
|
|
908
|
+
showUserPhoto = true,
|
|
909
|
+
requestAccess = true
|
|
910
|
+
}) {
|
|
911
|
+
const containerRef = useRef2(null);
|
|
912
|
+
const onAuthRef = useRef2(onAuth);
|
|
913
|
+
onAuthRef.current = onAuth;
|
|
914
|
+
useEffect2(() => {
|
|
915
|
+
const container = containerRef.current;
|
|
916
|
+
if (!container) return;
|
|
917
|
+
window[CALLBACK_NAME] = (user) => {
|
|
918
|
+
onAuthRef.current(user);
|
|
919
|
+
};
|
|
920
|
+
const script = document.createElement("script");
|
|
921
|
+
script.src = WIDGET_SCRIPT_URL;
|
|
922
|
+
script.async = true;
|
|
923
|
+
script.setAttribute("data-telegram-login", botUsername);
|
|
924
|
+
script.setAttribute("data-size", buttonSize);
|
|
925
|
+
script.setAttribute("data-onauth", `${CALLBACK_NAME}(user)`);
|
|
926
|
+
script.setAttribute("data-userpic", String(showUserPhoto));
|
|
927
|
+
if (requestAccess) {
|
|
928
|
+
script.setAttribute("data-request-access", "write");
|
|
929
|
+
}
|
|
930
|
+
if (cornerRadius !== void 0) {
|
|
931
|
+
script.setAttribute("data-radius", String(cornerRadius));
|
|
932
|
+
}
|
|
933
|
+
container.innerHTML = "";
|
|
934
|
+
container.appendChild(script);
|
|
935
|
+
return () => {
|
|
936
|
+
container.innerHTML = "";
|
|
937
|
+
delete window[CALLBACK_NAME];
|
|
938
|
+
};
|
|
939
|
+
}, [botUsername, buttonSize, cornerRadius, showUserPhoto, requestAccess]);
|
|
940
|
+
return /* @__PURE__ */ jsx4("div", { ref: containerRef });
|
|
941
|
+
}
|
|
942
|
+
|
|
586
943
|
// src/LoginModal.tsx
|
|
587
|
-
import { Fragment, jsx as
|
|
944
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
588
945
|
var MOBILE_BREAKPOINT = 640;
|
|
589
946
|
var btnStyle = {
|
|
590
947
|
display: "flex",
|
|
@@ -662,7 +1019,7 @@ function HoverButton({
|
|
|
662
1019
|
onClick,
|
|
663
1020
|
children
|
|
664
1021
|
}) {
|
|
665
|
-
return /* @__PURE__ */
|
|
1022
|
+
return /* @__PURE__ */ jsx5(
|
|
666
1023
|
motion.button,
|
|
667
1024
|
{
|
|
668
1025
|
type: "button",
|
|
@@ -674,7 +1031,14 @@ function HoverButton({
|
|
|
674
1031
|
);
|
|
675
1032
|
}
|
|
676
1033
|
function LoginModal({ onConnectWallet, walletIcon }) {
|
|
677
|
-
const {
|
|
1034
|
+
const {
|
|
1035
|
+
loginTelegram,
|
|
1036
|
+
loginModalOpen,
|
|
1037
|
+
closeLoginModal,
|
|
1038
|
+
botId,
|
|
1039
|
+
botUsername,
|
|
1040
|
+
useWidget
|
|
1041
|
+
} = useHypurrConnectInternal();
|
|
678
1042
|
const handleTelegramAuth = useCallback2(
|
|
679
1043
|
(user) => {
|
|
680
1044
|
loginTelegram(user);
|
|
@@ -682,7 +1046,7 @@ function LoginModal({ onConnectWallet, walletIcon }) {
|
|
|
682
1046
|
},
|
|
683
1047
|
[loginTelegram, closeLoginModal]
|
|
684
1048
|
);
|
|
685
|
-
|
|
1049
|
+
useEffect3(() => {
|
|
686
1050
|
if (!loginModalOpen) return;
|
|
687
1051
|
function onMessage(e) {
|
|
688
1052
|
if (e.origin !== "https://oauth.telegram.org") return;
|
|
@@ -721,7 +1085,7 @@ function LoginModal({ onConnectWallet, walletIcon }) {
|
|
|
721
1085
|
}, [botId]);
|
|
722
1086
|
const isMobile = useIsMobile();
|
|
723
1087
|
const modalContent = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
724
|
-
/* @__PURE__ */
|
|
1088
|
+
/* @__PURE__ */ jsx5(
|
|
725
1089
|
"div",
|
|
726
1090
|
{
|
|
727
1091
|
style: {
|
|
@@ -732,13 +1096,19 @@ function LoginModal({ onConnectWallet, walletIcon }) {
|
|
|
732
1096
|
gap: 8,
|
|
733
1097
|
overflow: "hidden"
|
|
734
1098
|
},
|
|
735
|
-
children: /* @__PURE__ */
|
|
736
|
-
|
|
1099
|
+
children: useWidget && botUsername ? /* @__PURE__ */ jsx5(
|
|
1100
|
+
TelegramLoginWidget,
|
|
1101
|
+
{
|
|
1102
|
+
botUsername,
|
|
1103
|
+
onAuth: handleTelegramAuth
|
|
1104
|
+
}
|
|
1105
|
+
) : /* @__PURE__ */ jsxs3(HoverButton, { onClick: openTelegramOAuth, children: [
|
|
1106
|
+
/* @__PURE__ */ jsx5(TelegramColorIcon, { style: iconSize }),
|
|
737
1107
|
"Telegram"
|
|
738
1108
|
] })
|
|
739
1109
|
}
|
|
740
1110
|
),
|
|
741
|
-
/* @__PURE__ */
|
|
1111
|
+
/* @__PURE__ */ jsx5("div", { style: dividerStyle }),
|
|
742
1112
|
/* @__PURE__ */ jsxs3(
|
|
743
1113
|
HoverButton,
|
|
744
1114
|
{
|
|
@@ -747,14 +1117,14 @@ function LoginModal({ onConnectWallet, walletIcon }) {
|
|
|
747
1117
|
onConnectWallet();
|
|
748
1118
|
},
|
|
749
1119
|
children: [
|
|
750
|
-
walletIcon ?? /* @__PURE__ */
|
|
1120
|
+
walletIcon ?? /* @__PURE__ */ jsx5(MetaMaskColorIcon, { style: iconSize }),
|
|
751
1121
|
"Wallet"
|
|
752
1122
|
]
|
|
753
1123
|
}
|
|
754
1124
|
)
|
|
755
1125
|
] });
|
|
756
|
-
return /* @__PURE__ */
|
|
757
|
-
/* @__PURE__ */
|
|
1126
|
+
return /* @__PURE__ */ jsx5(AnimatePresence, { children: loginModalOpen && (isMobile ? /* @__PURE__ */ jsx5(MobileDrawer, { onClose: closeLoginModal, children: modalContent }, "drawer") : /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
1127
|
+
/* @__PURE__ */ jsx5(
|
|
758
1128
|
motion.div,
|
|
759
1129
|
{
|
|
760
1130
|
style: backdropStyle,
|
|
@@ -766,7 +1136,7 @@ function LoginModal({ onConnectWallet, walletIcon }) {
|
|
|
766
1136
|
},
|
|
767
1137
|
"backdrop"
|
|
768
1138
|
),
|
|
769
|
-
/* @__PURE__ */
|
|
1139
|
+
/* @__PURE__ */ jsx5(
|
|
770
1140
|
motion.div,
|
|
771
1141
|
{
|
|
772
1142
|
style: modalWrapperStyle,
|
|
@@ -785,7 +1155,7 @@ function LoginModal({ onConnectWallet, walletIcon }) {
|
|
|
785
1155
|
transition: { duration: 0.2, ease: "easeOut" },
|
|
786
1156
|
onClick: (e) => e.stopPropagation(),
|
|
787
1157
|
children: [
|
|
788
|
-
/* @__PURE__ */
|
|
1158
|
+
/* @__PURE__ */ jsx5("p", { style: headingStyle, children: "Connect" }),
|
|
789
1159
|
modalContent
|
|
790
1160
|
]
|
|
791
1161
|
}
|
|
@@ -852,7 +1222,7 @@ function MobileDrawer({
|
|
|
852
1222
|
[onClose, controls]
|
|
853
1223
|
);
|
|
854
1224
|
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
855
|
-
/* @__PURE__ */
|
|
1225
|
+
/* @__PURE__ */ jsx5(
|
|
856
1226
|
motion.div,
|
|
857
1227
|
{
|
|
858
1228
|
style: backdropStyle,
|
|
@@ -877,9 +1247,9 @@ function MobileDrawer({
|
|
|
877
1247
|
dragElastic: { top: 0, bottom: 0.4 },
|
|
878
1248
|
onDragEnd: handleDragEnd,
|
|
879
1249
|
children: [
|
|
880
|
-
/* @__PURE__ */
|
|
881
|
-
/* @__PURE__ */
|
|
882
|
-
/* @__PURE__ */
|
|
1250
|
+
/* @__PURE__ */ jsx5("div", { style: drawerBgStyle }),
|
|
1251
|
+
/* @__PURE__ */ jsx5("div", { style: grabHandleAreaStyle, children: /* @__PURE__ */ jsx5("div", { style: grabHandleStyle }) }),
|
|
1252
|
+
/* @__PURE__ */ jsx5("p", { style: headingStyle, children: "Connect" }),
|
|
883
1253
|
children
|
|
884
1254
|
]
|
|
885
1255
|
},
|
|
@@ -887,10 +1257,21 @@ function MobileDrawer({
|
|
|
887
1257
|
)
|
|
888
1258
|
] });
|
|
889
1259
|
}
|
|
1260
|
+
|
|
1261
|
+
// src/types.ts
|
|
1262
|
+
function createEoaSigner(signTypedDataAsync, chainId) {
|
|
1263
|
+
const resolve = typeof signTypedDataAsync === "function" ? signTypedDataAsync : (args) => signTypedDataAsync.current(args);
|
|
1264
|
+
return {
|
|
1265
|
+
signTypedData: (params) => resolve(params),
|
|
1266
|
+
chainId
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
890
1269
|
export {
|
|
891
1270
|
GrpcExchangeTransport,
|
|
892
1271
|
HypurrConnectProvider,
|
|
893
1272
|
LoginModal,
|
|
1273
|
+
TelegramLoginWidget,
|
|
1274
|
+
createEoaSigner,
|
|
894
1275
|
createStaticClient,
|
|
895
1276
|
createTelegramClient,
|
|
896
1277
|
useHypurrConnect
|