@hfunlabs/hypurr-connect 0.1.11 → 0.1.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hfunlabs/hypurr-connect",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -24,7 +24,7 @@
24
24
  "format": "pnpm prettier --write ."
25
25
  },
26
26
  "peerDependencies": {
27
- "@hfunlabs/hyperliquid": "0.30.2-hfunlabs.2",
27
+ "@hfunlabs/hyperliquid": ">=0.30.2-hfunlabs.2 <0.33.0",
28
28
  "@protobuf-ts/grpcweb-transport": ">=2.0.0",
29
29
  "@protobuf-ts/runtime-rpc": ">=2.0.0",
30
30
  "framer-motion": ">=10.0.0",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "@eslint/js": "^9.39.3",
35
- "@hfunlabs/hyperliquid": "0.30.2-hfunlabs.2",
35
+ "@hfunlabs/hyperliquid": "0.32.2-hfunlabs.2",
36
36
  "@protobuf-ts/grpcweb-transport": "^2.11.1",
37
37
  "@protobuf-ts/runtime-rpc": "^2.11.1",
38
38
  "@types/react": "^19.2.14",
@@ -47,6 +47,7 @@
47
47
  "typescript-eslint": "^8.56.1"
48
48
  },
49
49
  "dependencies": {
50
- "hypurr-grpc": "github:Hypurr-Fun/hypurr-grpc#dev"
50
+ "hypurr-grpc": "github:Hypurr-Fun/hypurr-grpc#dev",
51
+ "viem": "^2.48.11"
51
52
  }
52
53
  }
@@ -0,0 +1,344 @@
1
+ import { AnimatePresence, motion } from "framer-motion";
2
+ import type { HyperliquidWallet } from "hypurr-grpc/ts/hypurr/wallet";
3
+ import {
4
+ useCallback,
5
+ useState,
6
+ type CSSProperties,
7
+ type ReactNode,
8
+ } from "react";
9
+ import {
10
+ AlertTriangle,
11
+ Loader2,
12
+ SpinKeyframes,
13
+ Trash2,
14
+ X,
15
+ } from "./icons/lucide";
16
+ import {
17
+ closeBtnStyle as makeCloseBtnStyle,
18
+ dangerOutlineButtonStyle,
19
+ fontFamily,
20
+ modalBackdropStyle,
21
+ modalHeaderStyle,
22
+ modalPanelStyle,
23
+ modalWrapperStyle,
24
+ profileColors,
25
+ titleStyle,
26
+ upperLabelStyle,
27
+ } from "./profileStyles";
28
+
29
+ export interface DeleteWalletModalProps {
30
+ isOpen: boolean;
31
+ onClose: () => void;
32
+ wallet: HyperliquidWallet | null;
33
+ onConfirm: (walletId: number) => Promise<void>;
34
+ /** Optional toast callback. Fires `{type:"success"}` on delete; errors are shown inline. */
35
+ onNotify?: (n: { type: "success" | "error"; message: string }) => void;
36
+ }
37
+
38
+ const DANGER_BG = "rgba(248,113,113,0.07)";
39
+ const DANGER_BORDER = "rgba(248,113,113,0.2)";
40
+
41
+ const backdropStyle = modalBackdropStyle(110);
42
+ const wrapperStyle = modalWrapperStyle(111, 16);
43
+ const panelStyle: CSSProperties = {
44
+ ...modalPanelStyle(true),
45
+ border: `1px solid ${profileColors.surfaceBd}`,
46
+ };
47
+ const headerStyle: CSSProperties = {
48
+ ...modalHeaderStyle,
49
+ borderBottom: "1px solid rgba(255,255,255,0.06)",
50
+ };
51
+
52
+ const bodyStyle: CSSProperties = {
53
+ padding: "20px 24px",
54
+ display: "flex",
55
+ flexDirection: "column",
56
+ gap: 16,
57
+ };
58
+
59
+ const warningBoxStyle: CSSProperties = {
60
+ display: "flex",
61
+ alignItems: "flex-start",
62
+ gap: 12,
63
+ padding: 14,
64
+ background: DANGER_BG,
65
+ border: `1px solid ${DANGER_BORDER}`,
66
+ borderRadius: 8,
67
+ };
68
+
69
+ const infoBoxStyle: CSSProperties = {
70
+ padding: "10px 12px",
71
+ background: "rgba(255,255,255,0.03)",
72
+ border: "1px solid rgba(255,255,255,0.06)",
73
+ borderRadius: 8,
74
+ };
75
+
76
+ const labelStyle: CSSProperties = {
77
+ display: "block",
78
+ fontSize: 12.5,
79
+ lineHeight: "1rem",
80
+ color: profileColors.muted,
81
+ marginBottom: 8,
82
+ };
83
+
84
+ const inputStyle = (disabled: boolean): CSSProperties => ({
85
+ width: "100%",
86
+ background: "rgba(13,18,25,0.9)",
87
+ border: `1px solid ${profileColors.surfaceBd}`,
88
+ borderRadius: 8,
89
+ padding: "10px 12px",
90
+ color: profileColors.text,
91
+ fontFamily: fontFamily.mono,
92
+ fontSize: 12.5,
93
+ lineHeight: "1rem",
94
+ outline: "none",
95
+ opacity: disabled ? 0.5 : 1,
96
+ boxSizing: "border-box",
97
+ transition: "border-color 150ms, background-color 150ms",
98
+ });
99
+
100
+ const footerStyle: CSSProperties = {
101
+ padding: "0 24px 24px",
102
+ };
103
+
104
+ const deleteButtonStyle = (
105
+ enabled: boolean,
106
+ hovered: boolean,
107
+ ): CSSProperties => ({
108
+ ...dangerOutlineButtonStyle(enabled, hovered),
109
+ width: "100%",
110
+ padding: "8px 0",
111
+ borderRadius: 8,
112
+ fontSize: 12.5,
113
+ lineHeight: "1rem",
114
+ fontWeight: 500,
115
+ display: "flex",
116
+ alignItems: "center",
117
+ justifyContent: "center",
118
+ gap: 8,
119
+ transition: "background-color 150ms, color 150ms, border-color 150ms",
120
+ });
121
+
122
+ export function DeleteWalletModal({
123
+ isOpen,
124
+ onClose,
125
+ wallet,
126
+ onConfirm,
127
+ onNotify,
128
+ }: DeleteWalletModalProps): ReactNode {
129
+ const [confirmName, setConfirmName] = useState("");
130
+ const [isDeleting, setIsDeleting] = useState(false);
131
+ const [error, setError] = useState<string | null>(null);
132
+ const [deleteHovered, setDeleteHovered] = useState(false);
133
+
134
+ const walletName = wallet?.name || "Unnamed Wallet";
135
+ const isNameMatch = confirmName === walletName;
136
+ const canDelete = isNameMatch && !isDeleting;
137
+
138
+ const handleClose = useCallback(() => {
139
+ if (isDeleting) return;
140
+ setConfirmName("");
141
+ setError(null);
142
+ onClose();
143
+ }, [isDeleting, onClose]);
144
+
145
+ const handleDelete = useCallback(async () => {
146
+ if (!wallet || !isNameMatch) return;
147
+ setError(null);
148
+ setIsDeleting(true);
149
+ try {
150
+ await onConfirm(wallet.id);
151
+ setConfirmName("");
152
+ onNotify?.({ type: "success", message: "Wallet deleted successfully" });
153
+ onClose();
154
+ } catch (e: unknown) {
155
+ setError(e instanceof Error ? e.message : "Failed to delete wallet");
156
+ } finally {
157
+ setIsDeleting(false);
158
+ }
159
+ }, [wallet, isNameMatch, onConfirm, onClose, onNotify]);
160
+
161
+ return (
162
+ <AnimatePresence>
163
+ {isOpen && wallet && (
164
+ <>
165
+ <SpinKeyframes />
166
+ <motion.div
167
+ key="backdrop"
168
+ style={backdropStyle}
169
+ initial={{ opacity: 0 }}
170
+ animate={{ opacity: 1 }}
171
+ exit={{ opacity: 0 }}
172
+ transition={{ duration: 0.15 }}
173
+ onClick={handleClose}
174
+ />
175
+ <div style={wrapperStyle}>
176
+ <motion.div
177
+ key="panel"
178
+ style={panelStyle}
179
+ initial={{ opacity: 0, y: 8 }}
180
+ animate={{ opacity: 1, y: 0 }}
181
+ exit={{ opacity: 0, y: 8 }}
182
+ transition={{ duration: 0.18, ease: "easeOut" }}
183
+ onClick={(e) => e.stopPropagation()}
184
+ >
185
+ <div style={headerStyle}>
186
+ <h3 style={titleStyle}>Delete Wallet</h3>
187
+ <button
188
+ onClick={handleClose}
189
+ disabled={isDeleting}
190
+ style={makeCloseBtnStyle(isDeleting)}
191
+ aria-label="Close"
192
+ >
193
+ <X size={16} />
194
+ </button>
195
+ </div>
196
+
197
+ <div style={bodyStyle}>
198
+ <div style={warningBoxStyle}>
199
+ <AlertTriangle
200
+ size={15}
201
+ color={profileColors.danger}
202
+ style={{ flexShrink: 0, marginTop: 2 }}
203
+ />
204
+ <div>
205
+ <p
206
+ style={{
207
+ margin: 0,
208
+ fontSize: 12.5,
209
+ lineHeight: "1rem",
210
+ color: profileColors.danger,
211
+ fontWeight: 500,
212
+ }}
213
+ >
214
+ This action cannot be undone
215
+ </p>
216
+ <p
217
+ style={{
218
+ margin: "2px 0 0",
219
+ fontSize: 12.5,
220
+ lineHeight: "1rem",
221
+ color: "rgba(248,113,113,0.7)",
222
+ }}
223
+ >
224
+ The private key will be permanently deleted. Any remaining
225
+ funds will be inaccessible.
226
+ </p>
227
+ </div>
228
+ </div>
229
+
230
+ <div style={infoBoxStyle}>
231
+ <p
232
+ style={{
233
+ margin: "0 0 4px",
234
+ color: profileColors.muted,
235
+ ...upperLabelStyle,
236
+ }}
237
+ >
238
+ Wallet to delete
239
+ </p>
240
+ <p
241
+ style={{
242
+ margin: 0,
243
+ fontSize: 12.5,
244
+ lineHeight: "1rem",
245
+ fontWeight: 500,
246
+ color: profileColors.text,
247
+ }}
248
+ >
249
+ {walletName}
250
+ </p>
251
+ <p
252
+ style={{
253
+ margin: "2px 0 0",
254
+ fontSize: 12.5,
255
+ lineHeight: "1rem",
256
+ color: profileColors.muted,
257
+ fontFamily: fontFamily.mono,
258
+ wordBreak: "break-all",
259
+ }}
260
+ >
261
+ {wallet.ethereumAddress}
262
+ </p>
263
+ </div>
264
+
265
+ <div>
266
+ <label style={labelStyle}>
267
+ Type{" "}
268
+ <span
269
+ style={{
270
+ color: profileColors.text,
271
+ fontWeight: 500,
272
+ }}
273
+ >
274
+ &quot;{walletName}&quot;
275
+ </span>{" "}
276
+ to confirm
277
+ </label>
278
+ <input
279
+ type="text"
280
+ value={confirmName}
281
+ onChange={(e) => setConfirmName(e.target.value)}
282
+ placeholder={walletName}
283
+ disabled={isDeleting}
284
+ style={inputStyle(isDeleting)}
285
+ />
286
+ </div>
287
+
288
+ {error && (
289
+ <div
290
+ style={{
291
+ display: "flex",
292
+ alignItems: "flex-start",
293
+ gap: 8,
294
+ padding: 12,
295
+ background: "rgba(248,113,113,0.08)",
296
+ border: `1px solid ${DANGER_BORDER}`,
297
+ borderRadius: 8,
298
+ }}
299
+ >
300
+ <AlertTriangle
301
+ size={14}
302
+ color={profileColors.danger}
303
+ style={{ flexShrink: 0, marginTop: 2 }}
304
+ />
305
+ <p
306
+ style={{
307
+ margin: 0,
308
+ fontSize: 12.5,
309
+ lineHeight: "1rem",
310
+ color: profileColors.danger,
311
+ }}
312
+ >
313
+ {error}
314
+ </p>
315
+ </div>
316
+ )}
317
+ </div>
318
+
319
+ <div style={footerStyle}>
320
+ <button
321
+ onClick={handleDelete}
322
+ disabled={!canDelete}
323
+ onMouseEnter={() => setDeleteHovered(true)}
324
+ onMouseLeave={() => setDeleteHovered(false)}
325
+ style={deleteButtonStyle(canDelete, deleteHovered)}
326
+ >
327
+ {isDeleting ? (
328
+ <>
329
+ <Loader2 size={14} /> Deleting...
330
+ </>
331
+ ) : (
332
+ <>
333
+ <Trash2 size={14} /> Delete Wallet
334
+ </>
335
+ )}
336
+ </button>
337
+ </div>
338
+ </motion.div>
339
+ </div>
340
+ </>
341
+ )}
342
+ </AnimatePresence>
343
+ );
344
+ }