@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/README.md +162 -40
- package/dist/index.d.ts +32 -6
- package/dist/index.js +464 -168
- package/dist/index.js.map +1 -1
- package/package.json +13 -11
- package/src/HypurrConnectProvider.tsx +332 -158
- package/src/LoginModal.tsx +162 -28
- package/src/agent.ts +80 -0
- package/src/icons/MetaMaskColorIcon.tsx +5 -3
- package/src/icons/TelegramColorIcon.tsx +8 -6
- package/src/index.ts +4 -0
- package/src/types.ts +38 -7
package/src/LoginModal.tsx
CHANGED
|
@@ -8,9 +8,10 @@ import {
|
|
|
8
8
|
useCallback,
|
|
9
9
|
useEffect,
|
|
10
10
|
useSyncExternalStore,
|
|
11
|
+
type CSSProperties,
|
|
11
12
|
type ReactNode,
|
|
12
13
|
} from "react";
|
|
13
|
-
import {
|
|
14
|
+
import { useHypurrConnectInternal } from "./HypurrConnectProvider";
|
|
14
15
|
import { MetaMaskColorIcon } from "./icons/MetaMaskColorIcon";
|
|
15
16
|
import { TelegramColorIcon } from "./icons/TelegramColorIcon";
|
|
16
17
|
import type { TelegramLoginData } from "./types";
|
|
@@ -22,8 +23,74 @@ export interface LoginModalProps {
|
|
|
22
23
|
|
|
23
24
|
const MOBILE_BREAKPOINT = 640;
|
|
24
25
|
|
|
25
|
-
const
|
|
26
|
-
"flex
|
|
26
|
+
const btnStyle: CSSProperties = {
|
|
27
|
+
display: "flex",
|
|
28
|
+
height: 53,
|
|
29
|
+
width: "100%",
|
|
30
|
+
alignItems: "center",
|
|
31
|
+
gap: 12,
|
|
32
|
+
overflow: "hidden",
|
|
33
|
+
borderRadius: 6,
|
|
34
|
+
background: "rgba(255,255,255,0.05)",
|
|
35
|
+
padding: "0 24px",
|
|
36
|
+
fontSize: 14,
|
|
37
|
+
fontWeight: 600,
|
|
38
|
+
letterSpacing: "-0.01em",
|
|
39
|
+
color: "#fff",
|
|
40
|
+
cursor: "pointer",
|
|
41
|
+
border: "none",
|
|
42
|
+
transition: "background 150ms",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const btnHoverBg = { background: "rgba(255,255,255,0.1)" };
|
|
46
|
+
|
|
47
|
+
const backdropStyle: CSSProperties = {
|
|
48
|
+
position: "fixed",
|
|
49
|
+
inset: 0,
|
|
50
|
+
zIndex: 100,
|
|
51
|
+
background: "rgba(0,0,0,0.6)",
|
|
52
|
+
backdropFilter: "blur(2px)",
|
|
53
|
+
WebkitBackdropFilter: "blur(2px)",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const modalWrapperStyle: CSSProperties = {
|
|
57
|
+
position: "fixed",
|
|
58
|
+
inset: 0,
|
|
59
|
+
zIndex: 101,
|
|
60
|
+
display: "flex",
|
|
61
|
+
alignItems: "center",
|
|
62
|
+
justifyContent: "center",
|
|
63
|
+
padding: 16,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const modalBoxStyle: CSSProperties = {
|
|
67
|
+
display: "flex",
|
|
68
|
+
width: 400,
|
|
69
|
+
flexDirection: "column",
|
|
70
|
+
alignItems: "center",
|
|
71
|
+
gap: 16,
|
|
72
|
+
overflow: "hidden",
|
|
73
|
+
borderRadius: 12,
|
|
74
|
+
border: "1px solid rgba(255,255,255,0.1)",
|
|
75
|
+
background: "#282828",
|
|
76
|
+
padding: 24,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const headingStyle: CSSProperties = {
|
|
80
|
+
fontSize: 16,
|
|
81
|
+
fontWeight: 700,
|
|
82
|
+
letterSpacing: "-0.025em",
|
|
83
|
+
color: "#fff",
|
|
84
|
+
margin: 0,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const dividerStyle: CSSProperties = {
|
|
88
|
+
height: 1,
|
|
89
|
+
width: "100%",
|
|
90
|
+
background: "rgba(255,255,255,0.05)",
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const iconSize: CSSProperties = { width: 20, height: 20 };
|
|
27
94
|
|
|
28
95
|
const mobileQuery =
|
|
29
96
|
typeof window !== "undefined"
|
|
@@ -43,9 +110,28 @@ function useIsMobile() {
|
|
|
43
110
|
return useSyncExternalStore(subscribeMobile, getSnapshotMobile, () => false);
|
|
44
111
|
}
|
|
45
112
|
|
|
113
|
+
function HoverButton({
|
|
114
|
+
onClick,
|
|
115
|
+
children,
|
|
116
|
+
}: {
|
|
117
|
+
onClick: () => void;
|
|
118
|
+
children: ReactNode;
|
|
119
|
+
}) {
|
|
120
|
+
return (
|
|
121
|
+
<motion.button
|
|
122
|
+
type="button"
|
|
123
|
+
onClick={onClick}
|
|
124
|
+
style={btnStyle}
|
|
125
|
+
whileHover={btnHoverBg}
|
|
126
|
+
>
|
|
127
|
+
{children}
|
|
128
|
+
</motion.button>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
46
132
|
export function LoginModal({ onConnectWallet, walletIcon }: LoginModalProps) {
|
|
47
133
|
const { loginTelegram, loginModalOpen, closeLoginModal, botId } =
|
|
48
|
-
|
|
134
|
+
useHypurrConnectInternal();
|
|
49
135
|
|
|
50
136
|
const handleTelegramAuth = useCallback(
|
|
51
137
|
(user: TelegramLoginData) => {
|
|
@@ -99,26 +185,33 @@ export function LoginModal({ onConnectWallet, walletIcon }: LoginModalProps) {
|
|
|
99
185
|
|
|
100
186
|
const modalContent = (
|
|
101
187
|
<>
|
|
102
|
-
<div
|
|
103
|
-
|
|
104
|
-
|
|
188
|
+
<div
|
|
189
|
+
style={{
|
|
190
|
+
display: "flex",
|
|
191
|
+
width: "100%",
|
|
192
|
+
flexDirection: "column",
|
|
193
|
+
alignItems: "center",
|
|
194
|
+
gap: 8,
|
|
195
|
+
overflow: "hidden",
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
198
|
+
<HoverButton onClick={openTelegramOAuth}>
|
|
199
|
+
<TelegramColorIcon style={iconSize} />
|
|
105
200
|
Telegram
|
|
106
|
-
</
|
|
201
|
+
</HoverButton>
|
|
107
202
|
</div>
|
|
108
203
|
|
|
109
|
-
<div
|
|
204
|
+
<div style={dividerStyle} />
|
|
110
205
|
|
|
111
|
-
<
|
|
112
|
-
type="button"
|
|
206
|
+
<HoverButton
|
|
113
207
|
onClick={() => {
|
|
114
208
|
closeLoginModal();
|
|
115
209
|
onConnectWallet();
|
|
116
210
|
}}
|
|
117
|
-
className={btnClass}
|
|
118
211
|
>
|
|
119
|
-
{walletIcon ?? <MetaMaskColorIcon
|
|
212
|
+
{walletIcon ?? <MetaMaskColorIcon style={iconSize} />}
|
|
120
213
|
Wallet
|
|
121
|
-
</
|
|
214
|
+
</HoverButton>
|
|
122
215
|
</>
|
|
123
216
|
);
|
|
124
217
|
|
|
@@ -133,7 +226,7 @@ export function LoginModal({ onConnectWallet, walletIcon }: LoginModalProps) {
|
|
|
133
226
|
<>
|
|
134
227
|
<motion.div
|
|
135
228
|
key="backdrop"
|
|
136
|
-
|
|
229
|
+
style={backdropStyle}
|
|
137
230
|
initial={{ opacity: 0 }}
|
|
138
231
|
animate={{ opacity: 1 }}
|
|
139
232
|
exit={{ opacity: 0 }}
|
|
@@ -142,7 +235,7 @@ export function LoginModal({ onConnectWallet, walletIcon }: LoginModalProps) {
|
|
|
142
235
|
/>
|
|
143
236
|
<motion.div
|
|
144
237
|
key="modal-wrapper"
|
|
145
|
-
|
|
238
|
+
style={modalWrapperStyle}
|
|
146
239
|
initial={{ opacity: 0 }}
|
|
147
240
|
animate={{ opacity: 1 }}
|
|
148
241
|
exit={{ opacity: 0 }}
|
|
@@ -150,16 +243,14 @@ export function LoginModal({ onConnectWallet, walletIcon }: LoginModalProps) {
|
|
|
150
243
|
onClick={closeLoginModal}
|
|
151
244
|
>
|
|
152
245
|
<motion.div
|
|
153
|
-
|
|
246
|
+
style={modalBoxStyle}
|
|
154
247
|
initial={{ opacity: 0, scale: 0.95, y: 10 }}
|
|
155
248
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
156
249
|
exit={{ opacity: 0, scale: 0.95, y: 10 }}
|
|
157
250
|
transition={{ duration: 0.2, ease: "easeOut" }}
|
|
158
251
|
onClick={(e) => e.stopPropagation()}
|
|
159
252
|
>
|
|
160
|
-
<p
|
|
161
|
-
Connect
|
|
162
|
-
</p>
|
|
253
|
+
<p style={headingStyle}>Connect</p>
|
|
163
254
|
{modalContent}
|
|
164
255
|
</motion.div>
|
|
165
256
|
</motion.div>
|
|
@@ -169,6 +260,51 @@ export function LoginModal({ onConnectWallet, walletIcon }: LoginModalProps) {
|
|
|
169
260
|
);
|
|
170
261
|
}
|
|
171
262
|
|
|
263
|
+
const drawerSheetStyle: CSSProperties = {
|
|
264
|
+
position: "fixed",
|
|
265
|
+
left: 0,
|
|
266
|
+
right: 0,
|
|
267
|
+
bottom: 0,
|
|
268
|
+
zIndex: 101,
|
|
269
|
+
display: "flex",
|
|
270
|
+
flexDirection: "column",
|
|
271
|
+
alignItems: "center",
|
|
272
|
+
gap: 16,
|
|
273
|
+
borderTopLeftRadius: 12,
|
|
274
|
+
borderTopRightRadius: 12,
|
|
275
|
+
borderLeft: "1px solid rgba(255,255,255,0.1)",
|
|
276
|
+
borderRight: "1px solid rgba(255,255,255,0.1)",
|
|
277
|
+
borderTop: "1px solid rgba(255,255,255,0.1)",
|
|
278
|
+
background: "#282828",
|
|
279
|
+
padding: "12px 24px max(24px, env(safe-area-inset-bottom))",
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const drawerBgStyle: CSSProperties = {
|
|
283
|
+
position: "absolute",
|
|
284
|
+
left: 0,
|
|
285
|
+
right: 0,
|
|
286
|
+
top: 0,
|
|
287
|
+
bottom: "-100vh",
|
|
288
|
+
zIndex: -1,
|
|
289
|
+
background: "#282828",
|
|
290
|
+
borderTopLeftRadius: 12,
|
|
291
|
+
borderTopRightRadius: 12,
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const grabHandleAreaStyle: CSSProperties = {
|
|
295
|
+
width: "100%",
|
|
296
|
+
cursor: "grab",
|
|
297
|
+
paddingBottom: 4,
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const grabHandleStyle: CSSProperties = {
|
|
301
|
+
margin: "0 auto",
|
|
302
|
+
height: 4,
|
|
303
|
+
width: 100,
|
|
304
|
+
borderRadius: 9999,
|
|
305
|
+
background: "rgba(255,255,255,0.05)",
|
|
306
|
+
};
|
|
307
|
+
|
|
172
308
|
function MobileDrawer({
|
|
173
309
|
children,
|
|
174
310
|
onClose,
|
|
@@ -193,7 +329,7 @@ function MobileDrawer({
|
|
|
193
329
|
<>
|
|
194
330
|
<motion.div
|
|
195
331
|
key="drawer-backdrop"
|
|
196
|
-
|
|
332
|
+
style={backdropStyle}
|
|
197
333
|
initial={{ opacity: 0 }}
|
|
198
334
|
animate={{ opacity: 1 }}
|
|
199
335
|
exit={{ opacity: 0 }}
|
|
@@ -203,7 +339,7 @@ function MobileDrawer({
|
|
|
203
339
|
|
|
204
340
|
<motion.div
|
|
205
341
|
key="drawer-sheet"
|
|
206
|
-
|
|
342
|
+
style={drawerSheetStyle}
|
|
207
343
|
initial={{ y: "100%" }}
|
|
208
344
|
animate={{ y: 0 }}
|
|
209
345
|
exit={{ y: "100%" }}
|
|
@@ -213,15 +349,13 @@ function MobileDrawer({
|
|
|
213
349
|
dragElastic={{ top: 0, bottom: 0.4 }}
|
|
214
350
|
onDragEnd={handleDragEnd}
|
|
215
351
|
>
|
|
216
|
-
<div
|
|
352
|
+
<div style={drawerBgStyle} />
|
|
217
353
|
|
|
218
|
-
<div
|
|
219
|
-
<div
|
|
354
|
+
<div style={grabHandleAreaStyle}>
|
|
355
|
+
<div style={grabHandleStyle} />
|
|
220
356
|
</div>
|
|
221
357
|
|
|
222
|
-
<p
|
|
223
|
-
Connect
|
|
224
|
-
</p>
|
|
358
|
+
<p style={headingStyle}>Connect</p>
|
|
225
359
|
|
|
226
360
|
{children}
|
|
227
361
|
</motion.div>
|
package/src/agent.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { StoredAgent } from "./types";
|
|
2
2
|
|
|
3
|
+
export const AGENT_NAME = "hypurr-connect";
|
|
4
|
+
|
|
3
5
|
const AGENT_STORAGE_PREFIX = "hypurr-connect-agent";
|
|
4
6
|
|
|
5
7
|
function storageKey(masterAddress: string): string {
|
|
@@ -41,3 +43,81 @@ export async function generateAgentKey(): Promise<{
|
|
|
41
43
|
const signer = new PrivateKeySigner(privateKey);
|
|
42
44
|
return { privateKey, address: signer.address };
|
|
43
45
|
}
|
|
46
|
+
|
|
47
|
+
interface ExtraAgent {
|
|
48
|
+
address: string;
|
|
49
|
+
name: string;
|
|
50
|
+
validUntil: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Query the Hyperliquid info API for the named agents registered to a user.
|
|
55
|
+
* Returns the matching entry for AGENT_NAME if it exists and is still valid.
|
|
56
|
+
*/
|
|
57
|
+
export async function fetchActiveAgent(
|
|
58
|
+
userAddress: string,
|
|
59
|
+
isTestnet: boolean,
|
|
60
|
+
): Promise<ExtraAgent | null> {
|
|
61
|
+
const url = isTestnet
|
|
62
|
+
? "https://api.hyperliquid-testnet.xyz/info"
|
|
63
|
+
: "https://api.hyperliquid.xyz/info";
|
|
64
|
+
|
|
65
|
+
const res = await fetch(url, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: { "Content-Type": "application/json" },
|
|
68
|
+
body: JSON.stringify({ type: "extraAgents", user: userAddress }),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (!res.ok) return null;
|
|
72
|
+
|
|
73
|
+
const agents: unknown = await res.json();
|
|
74
|
+
if (!Array.isArray(agents)) return null;
|
|
75
|
+
|
|
76
|
+
const nowMs = Date.now();
|
|
77
|
+
const match = (agents as ExtraAgent[]).find(
|
|
78
|
+
(a) => a.name === AGENT_NAME && a.validUntil * 1000 > nowMs,
|
|
79
|
+
);
|
|
80
|
+
if (!match) return null;
|
|
81
|
+
return { ...match, validUntil: match.validUntil * 1000 };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Checks whether a stored agent is still valid: the address must appear in the
|
|
86
|
+
* on-chain `extraAgents` list and not be expired.
|
|
87
|
+
*/
|
|
88
|
+
export async function isAgentValid(
|
|
89
|
+
stored: StoredAgent,
|
|
90
|
+
userAddress: string,
|
|
91
|
+
isTestnet: boolean,
|
|
92
|
+
): Promise<boolean> {
|
|
93
|
+
if (stored.validUntil <= Date.now()) return false;
|
|
94
|
+
|
|
95
|
+
const remote = await fetchActiveAgent(userAddress, isTestnet);
|
|
96
|
+
if (!remote) return false;
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
remote.address.toLowerCase() === stored.address.toLowerCase() &&
|
|
100
|
+
remote.validUntil > Date.now()
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const DEAD_AGENT_PATTERNS = [
|
|
105
|
+
/agent address .+ is not valid/i,
|
|
106
|
+
/unknown signer/i,
|
|
107
|
+
/not authorized/i,
|
|
108
|
+
/not an agent/i,
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Returns true if the error indicates the agent has been pruned, expired,
|
|
113
|
+
* or is otherwise no longer registered on-chain.
|
|
114
|
+
*/
|
|
115
|
+
export function isDeadAgentError(err: unknown): boolean {
|
|
116
|
+
const msg =
|
|
117
|
+
err instanceof Error
|
|
118
|
+
? err.message
|
|
119
|
+
: typeof err === "object" && err !== null && "message" in err
|
|
120
|
+
? String((err as { message: unknown }).message)
|
|
121
|
+
: String(err);
|
|
122
|
+
return DEAD_AGENT_PATTERNS.some((p) => p.test(msg));
|
|
123
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
export function MetaMaskColorIcon({ style }: { style?: CSSProperties }) {
|
|
2
4
|
return (
|
|
3
5
|
<svg
|
|
4
6
|
width="24"
|
|
5
7
|
height="24"
|
|
6
8
|
viewBox="0 0 24 24"
|
|
7
|
-
|
|
9
|
+
style={style}
|
|
8
10
|
fill="none"
|
|
9
11
|
xmlns="http://www.w3.org/2000/svg"
|
|
10
12
|
>
|
|
11
|
-
<g
|
|
13
|
+
<g clipPath="url(#clip0_2567_1088)">
|
|
12
14
|
<path
|
|
13
15
|
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"
|
|
14
16
|
fill="#FF5C16"
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
export function TelegramColorIcon({ style }: { style?: CSSProperties }) {
|
|
2
4
|
return (
|
|
3
5
|
<svg
|
|
4
6
|
width="24"
|
|
5
7
|
height="24"
|
|
6
8
|
viewBox="0 0 24 24"
|
|
7
|
-
|
|
9
|
+
style={style}
|
|
8
10
|
fill="none"
|
|
9
11
|
xmlns="http://www.w3.org/2000/svg"
|
|
10
12
|
>
|
|
@@ -13,8 +15,8 @@ export function TelegramColorIcon({ className }: { className?: string }) {
|
|
|
13
15
|
fill="url(#paint0_linear_2571_1084)"
|
|
14
16
|
/>
|
|
15
17
|
<path
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
fillRule="evenodd"
|
|
19
|
+
clipRule="evenodd"
|
|
18
20
|
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"
|
|
19
21
|
fill="white"
|
|
20
22
|
/>
|
|
@@ -27,8 +29,8 @@ export function TelegramColorIcon({ className }: { className?: string }) {
|
|
|
27
29
|
y2="1789.65"
|
|
28
30
|
gradientUnits="userSpaceOnUse"
|
|
29
31
|
>
|
|
30
|
-
<stop
|
|
31
|
-
<stop offset="1"
|
|
32
|
+
<stop stopColor="#2AABEE" />
|
|
33
|
+
<stop offset="1" stopColor="#229ED9" />
|
|
32
34
|
</linearGradient>
|
|
33
35
|
</defs>
|
|
34
36
|
</svg>
|
package/src/index.ts
CHANGED
|
@@ -16,3 +16,7 @@ export type {
|
|
|
16
16
|
StoredAgent,
|
|
17
17
|
TelegramLoginData,
|
|
18
18
|
} from "./types";
|
|
19
|
+
|
|
20
|
+
// Re-export wallet types from hypurr-grpc so consumers don't need the dependency
|
|
21
|
+
export type { HyperliquidWallet } from "hypurr-grpc/ts/hypurr/wallet";
|
|
22
|
+
export type { TelegramChatWalletPack } from "hypurr-grpc/ts/hypurr/user";
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ExchangeClient } from "@hfunlabs/hyperliquid";
|
|
2
2
|
import type { StaticClient } from "hypurr-grpc/ts/hypurr/static/static_service.client";
|
|
3
3
|
import type { TelegramClient } from "hypurr-grpc/ts/hypurr/telegram/telegram_service.client";
|
|
4
|
+
import type { HyperliquidWallet } from "hypurr-grpc/ts/hypurr/wallet";
|
|
5
|
+
import type { TelegramChatWalletPack } from "hypurr-grpc/ts/hypurr/user";
|
|
4
6
|
|
|
5
7
|
// ─── Config ──────────────────────────────────────────────────────
|
|
6
8
|
|
|
@@ -36,6 +38,8 @@ export interface HypurrUser {
|
|
|
36
38
|
photoUrl?: string;
|
|
37
39
|
authMethod: AuthMethod;
|
|
38
40
|
telegramId?: string;
|
|
41
|
+
hfunScore?: number;
|
|
42
|
+
reputationScore?: number;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
// ─── Agent (EOA flow) ────────────────────────────────────────────
|
|
@@ -44,6 +48,8 @@ export interface StoredAgent {
|
|
|
44
48
|
privateKey: `0x${string}`;
|
|
45
49
|
address: `0x${string}`;
|
|
46
50
|
approvedAt: number;
|
|
51
|
+
/** Epoch ms from the `extraAgents` response; agent is invalid after this time. */
|
|
52
|
+
validUntil: number;
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
export type SignTypedDataFn = (params: {
|
|
@@ -69,10 +75,33 @@ export interface HypurrConnectState {
|
|
|
69
75
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
76
|
exchange: ExchangeClient<any> | null;
|
|
71
77
|
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
// Multi-wallet (Telegram only — EOA has a single wallet)
|
|
79
|
+
wallets: HyperliquidWallet[];
|
|
80
|
+
selectedWalletId: number;
|
|
81
|
+
selectWallet: (walletId: number) => void;
|
|
82
|
+
|
|
83
|
+
// Wallet management (Telegram only)
|
|
84
|
+
createWallet: (name: string) => Promise<HyperliquidWallet>;
|
|
85
|
+
deleteWallet: (walletId: number) => Promise<void>;
|
|
86
|
+
refreshWallets: () => void;
|
|
87
|
+
|
|
88
|
+
// Wallet packs & labels (Telegram only)
|
|
89
|
+
packs: TelegramChatWalletPack[];
|
|
90
|
+
createWalletPack: (name: string) => Promise<number>;
|
|
91
|
+
addPackLabel: (params: {
|
|
92
|
+
walletAddress: string;
|
|
93
|
+
walletLabel: string;
|
|
94
|
+
packId: number;
|
|
95
|
+
}) => Promise<void>;
|
|
96
|
+
modifyPackLabel: (params: {
|
|
97
|
+
walletLabelOld: string;
|
|
98
|
+
walletLabelNew: string;
|
|
99
|
+
packId: number;
|
|
100
|
+
}) => Promise<void>;
|
|
101
|
+
removePackLabel: (params: {
|
|
102
|
+
walletLabel: string;
|
|
103
|
+
packId: number;
|
|
104
|
+
}) => Promise<void>;
|
|
76
105
|
|
|
77
106
|
// Login modal
|
|
78
107
|
loginModalOpen: boolean;
|
|
@@ -80,14 +109,16 @@ export interface HypurrConnectState {
|
|
|
80
109
|
closeLoginModal: () => void;
|
|
81
110
|
|
|
82
111
|
// Auth actions
|
|
83
|
-
|
|
84
|
-
|
|
112
|
+
connectEoa: (address: `0x${string}`) => void;
|
|
113
|
+
approveAgent: (
|
|
114
|
+
signTypedDataAsync: SignTypedDataFn,
|
|
115
|
+
chainId: number,
|
|
116
|
+
) => Promise<void>;
|
|
85
117
|
logout: () => void;
|
|
86
118
|
|
|
87
119
|
// EOA agent management
|
|
88
120
|
agent: StoredAgent | null;
|
|
89
121
|
agentReady: boolean;
|
|
90
|
-
approveAgent: (signTypedDataAsync: SignTypedDataFn) => Promise<void>;
|
|
91
122
|
clearAgent: () => void;
|
|
92
123
|
|
|
93
124
|
// Telegram config
|