@rialo/frost 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/dist/index.cjs ADDED
@@ -0,0 +1,1025 @@
1
+ 'use strict';
2
+
3
+ var frostCore = require('@rialo/frost-core');
4
+ var reactQuery = require('@tanstack/react-query');
5
+ var react = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+ var reactStore = require('@tanstack/react-store');
8
+ var tsCdk = require('@rialo/ts-cdk');
9
+
10
+ // src/index.ts
11
+ var FrostContext = react.createContext(null);
12
+ function FrostProvider({
13
+ children,
14
+ config,
15
+ queryClient: externalQueryClient
16
+ }) {
17
+ const queryClientRef = react.useRef(null);
18
+ if (!queryClientRef.current && !externalQueryClient) {
19
+ queryClientRef.current = new reactQuery.QueryClient({
20
+ defaultOptions: {
21
+ queries: {
22
+ staleTime: 1e3 * 60,
23
+ // 1 minute
24
+ gcTime: 1e3 * 60 * 5
25
+ // 5 minutes
26
+ }
27
+ }
28
+ });
29
+ }
30
+ const queryClient = externalQueryClient ?? queryClientRef.current;
31
+ react.useEffect(() => {
32
+ if (!config._registry) {
33
+ const registry = new frostCore.WalletRegistry(config);
34
+ const bridge = new frostCore.WalletEventBridge(config);
35
+ frostCore.initializeConfig(config, registry, bridge);
36
+ }
37
+ const shouldAutoReconnect = config.options.autoConnect && config.store.state.walletName && !config.store.state.userDisconnected;
38
+ if (shouldAutoReconnect) {
39
+ void frostCore.reconnect(config);
40
+ }
41
+ }, [config]);
42
+ return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsxRuntime.jsx(FrostContext.Provider, { value: config, children }) });
43
+ }
44
+ function useFrostConfig() {
45
+ const config = react.useContext(FrostContext);
46
+ if (!config) {
47
+ throw new Error("useFrostConfig must be used within a FrostProvider");
48
+ }
49
+ return config;
50
+ }
51
+ var selectActiveAccount = (state) => {
52
+ if (!state.accountAddress) return null;
53
+ return state.accounts.get(state.accountAddress) ?? null;
54
+ };
55
+ function useActiveAccount() {
56
+ const config = useFrostConfig();
57
+ return reactStore.useStore(config.store, selectActiveAccount);
58
+ }
59
+ var selectActiveWallet = (state) => {
60
+ if (!state.walletName) return null;
61
+ return state.wallets.get(state.walletName) ?? null;
62
+ };
63
+ function useActiveWallet() {
64
+ const config = useFrostConfig();
65
+ return reactStore.useStore(config.store, selectActiveWallet);
66
+ }
67
+ var selectStatus = (state) => state.status;
68
+ var selectIsConnected = (state) => state.status === "connected";
69
+ function useConnectionStatus() {
70
+ const config = useFrostConfig();
71
+ return reactStore.useStore(config.store, selectStatus);
72
+ }
73
+ function useIsConnected() {
74
+ const config = useFrostConfig();
75
+ return reactStore.useStore(config.store, selectIsConnected);
76
+ }
77
+ function useConnectWallet(options) {
78
+ const config = useFrostConfig();
79
+ return reactQuery.useMutation({
80
+ mutationKey: ["frost", "connect"],
81
+ mutationFn: (connectOptions) => frostCore.connect(config, connectOptions),
82
+ onSuccess: options?.onSuccess,
83
+ onError: options?.onError
84
+ });
85
+ }
86
+ function useDisconnectWallet(options) {
87
+ const config = useFrostConfig();
88
+ return reactQuery.useMutation({
89
+ mutationKey: ["frost", "disconnect"],
90
+ mutationFn: () => frostCore.disconnect(config),
91
+ onSuccess: options?.onSuccess,
92
+ onError: options?.onError
93
+ });
94
+ }
95
+ function CloseIcon() {
96
+ return /* @__PURE__ */ jsxRuntime.jsx(
97
+ "svg",
98
+ {
99
+ width: "20",
100
+ height: "20",
101
+ viewBox: "0 0 20 20",
102
+ fill: "none",
103
+ xmlns: "http://www.w3.org/2000/svg",
104
+ role: "img",
105
+ "aria-label": "Close modal",
106
+ children: /* @__PURE__ */ jsxRuntime.jsx(
107
+ "path",
108
+ {
109
+ d: "M15 5L5 15M5 5L15 15",
110
+ stroke: "currentColor",
111
+ strokeWidth: "1.5",
112
+ strokeLinecap: "round",
113
+ strokeLinejoin: "round"
114
+ }
115
+ )
116
+ }
117
+ );
118
+ }
119
+ function WalletPlaceholderIcon() {
120
+ return /* @__PURE__ */ jsxRuntime.jsxs(
121
+ "svg",
122
+ {
123
+ width: "24",
124
+ height: "24",
125
+ viewBox: "0 0 24 24",
126
+ fill: "none",
127
+ xmlns: "http://www.w3.org/2000/svg",
128
+ role: "img",
129
+ "aria-label": "Wallet placeholder icon",
130
+ children: [
131
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "6", width: "20", height: "14", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
132
+ /* @__PURE__ */ jsxRuntime.jsx(
133
+ "path",
134
+ {
135
+ d: "M6 6V5C6 3.89543 6.89543 3 8 3H16C17.1046 3 18 3.89543 18 5V6",
136
+ stroke: "currentColor",
137
+ strokeWidth: "1.5"
138
+ }
139
+ ),
140
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "16", cy: "13", r: "1.5", fill: "currentColor" })
141
+ ]
142
+ }
143
+ );
144
+ }
145
+ function DisconnectIcon() {
146
+ return /* @__PURE__ */ jsxRuntime.jsxs(
147
+ "svg",
148
+ {
149
+ width: "14",
150
+ height: "14",
151
+ viewBox: "0 0 14 14",
152
+ fill: "none",
153
+ xmlns: "http://www.w3.org/2000/svg",
154
+ role: "img",
155
+ "aria-label": "Disconnect icon",
156
+ children: [
157
+ /* @__PURE__ */ jsxRuntime.jsx(
158
+ "path",
159
+ {
160
+ d: "M5.25 12.25H3.5C2.53333 12.25 1.75 11.4667 1.75 10.5V3.5C1.75 2.53333 2.53333 1.75 3.5 1.75H5.25",
161
+ stroke: "currentColor",
162
+ strokeWidth: "1.25",
163
+ strokeLinecap: "round"
164
+ }
165
+ ),
166
+ /* @__PURE__ */ jsxRuntime.jsx(
167
+ "path",
168
+ {
169
+ d: "M9.33331 9.91669L12.25 7.00002L9.33331 4.08335",
170
+ stroke: "currentColor",
171
+ strokeWidth: "1.25",
172
+ strokeLinecap: "round",
173
+ strokeLinejoin: "round"
174
+ }
175
+ ),
176
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12.25 7H5.25", stroke: "currentColor", strokeWidth: "1.25", strokeLinecap: "round" })
177
+ ]
178
+ }
179
+ );
180
+ }
181
+ function ChevronIcon({ direction }) {
182
+ return /* @__PURE__ */ jsxRuntime.jsx(
183
+ "svg",
184
+ {
185
+ width: "12",
186
+ height: "12",
187
+ viewBox: "0 0 12 12",
188
+ fill: "none",
189
+ xmlns: "http://www.w3.org/2000/svg",
190
+ role: "img",
191
+ "aria-label": "Chevron icon",
192
+ style: {
193
+ marginLeft: "0.25rem",
194
+ transition: "transform 0.15s ease",
195
+ transform: direction === "up" ? "rotate(180deg)" : void 0
196
+ },
197
+ children: /* @__PURE__ */ jsxRuntime.jsx(
198
+ "path",
199
+ {
200
+ d: "M2.5 4.5L6 8L9.5 4.5",
201
+ stroke: "currentColor",
202
+ strokeWidth: "1.5",
203
+ strokeLinecap: "round",
204
+ strokeLinejoin: "round"
205
+ }
206
+ )
207
+ }
208
+ );
209
+ }
210
+ function CopyIcon() {
211
+ return /* @__PURE__ */ jsxRuntime.jsxs(
212
+ "svg",
213
+ {
214
+ width: "14",
215
+ height: "14",
216
+ viewBox: "0 0 14 14",
217
+ fill: "none",
218
+ xmlns: "http://www.w3.org/2000/svg",
219
+ role: "img",
220
+ "aria-label": "Copy icon",
221
+ children: [
222
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", stroke: "currentColor", strokeWidth: "1.25" }),
223
+ /* @__PURE__ */ jsxRuntime.jsx(
224
+ "path",
225
+ {
226
+ d: "M10 4V3C10 2.17157 9.32843 1.5 8.5 1.5H3C2.17157 1.5 1.5 2.17157 1.5 3V8.5C1.5 9.32843 2.17157 10 3 10H4",
227
+ stroke: "currentColor",
228
+ strokeWidth: "1.25"
229
+ }
230
+ )
231
+ ]
232
+ }
233
+ );
234
+ }
235
+ var selectWalletsMap = (state) => state.wallets;
236
+ var selectWalletsReady = (state) => state.wallets.size > 0;
237
+ function useWallets() {
238
+ const config = useFrostConfig();
239
+ const walletsMap = reactStore.useStore(config.store, selectWalletsMap);
240
+ return react.useMemo(
241
+ () => Array.from(walletsMap.values()).sort((a, b) => {
242
+ const priorityDiff = (b.priority ?? 0) - (a.priority ?? 0);
243
+ if (priorityDiff !== 0) return priorityDiff;
244
+ return (b.lastConnectedAt ?? 0) - (a.lastConnectedAt ?? 0);
245
+ }),
246
+ [walletsMap]
247
+ );
248
+ }
249
+ function useWalletsReady() {
250
+ const config = useFrostConfig();
251
+ return reactStore.useStore(config.store, selectWalletsReady);
252
+ }
253
+ var defaultStyles = {
254
+ overlay: {
255
+ position: "fixed",
256
+ inset: 0,
257
+ backgroundColor: "var(--frost-overlay-bg, rgba(0, 0, 0, 0.5))",
258
+ display: "flex",
259
+ alignItems: "center",
260
+ justifyContent: "center",
261
+ zIndex: "var(--frost-z-index, 9999)",
262
+ padding: "1rem"
263
+ },
264
+ content: {
265
+ backgroundColor: "var(--frost-modal-bg, #ffffff)",
266
+ borderRadius: "var(--frost-border-radius, 12px)",
267
+ boxShadow: "var(--frost-shadow, 0 4px 24px rgba(0, 0, 0, 0.15))",
268
+ maxWidth: "var(--frost-modal-max-width, 400px)",
269
+ width: "100%",
270
+ maxHeight: "80vh",
271
+ overflow: "hidden",
272
+ display: "flex",
273
+ flexDirection: "column"
274
+ },
275
+ header: {
276
+ display: "flex",
277
+ alignItems: "center",
278
+ justifyContent: "space-between",
279
+ padding: "1rem 1.25rem",
280
+ borderBottom: "1px solid var(--frost-border-color, #e5e7eb)"
281
+ },
282
+ title: {
283
+ margin: 0,
284
+ fontSize: "1.125rem",
285
+ fontWeight: 600,
286
+ color: "var(--frost-text-color, #111827)"
287
+ },
288
+ closeButton: {
289
+ background: "none",
290
+ border: "none",
291
+ padding: "0.5rem",
292
+ cursor: "pointer",
293
+ borderRadius: "var(--frost-border-radius-sm, 6px)",
294
+ color: "var(--frost-text-secondary, #6b7280)",
295
+ display: "flex",
296
+ alignItems: "center",
297
+ justifyContent: "center",
298
+ transition: "background-color 0.15s ease"
299
+ },
300
+ walletList: {
301
+ listStyle: "none",
302
+ margin: 0,
303
+ padding: "0.5rem",
304
+ overflowY: "auto",
305
+ flex: 1
306
+ },
307
+ walletItem: {
308
+ display: "flex",
309
+ alignItems: "center",
310
+ gap: "0.75rem",
311
+ padding: "0.875rem 1rem",
312
+ borderRadius: "var(--frost-border-radius-sm, 8px)",
313
+ cursor: "pointer",
314
+ border: "none",
315
+ background: "none",
316
+ width: "100%",
317
+ textAlign: "left",
318
+ transition: "background-color 0.15s ease",
319
+ color: "var(--frost-text-color, #111827)"
320
+ },
321
+ walletIcon: {
322
+ width: 40,
323
+ height: 40,
324
+ borderRadius: "var(--frost-border-radius-sm, 8px)",
325
+ objectFit: "contain",
326
+ backgroundColor: "var(--frost-icon-bg, #f3f4f6)"
327
+ },
328
+ walletInfo: {
329
+ flex: 1,
330
+ minWidth: 0
331
+ },
332
+ walletName: {
333
+ margin: 0,
334
+ fontSize: "0.9375rem",
335
+ fontWeight: 500,
336
+ color: "inherit"
337
+ },
338
+ walletStatus: {
339
+ margin: 0,
340
+ fontSize: "0.8125rem",
341
+ color: "var(--frost-text-secondary, #6b7280)"
342
+ },
343
+ spinner: {
344
+ width: 20,
345
+ height: 20,
346
+ border: "2px solid var(--frost-border-color, #e5e7eb)",
347
+ borderTopColor: "var(--frost-primary, #3b82f6)",
348
+ borderRadius: "50%",
349
+ animation: "frost-spin 0.8s linear infinite"
350
+ },
351
+ emptyState: {
352
+ padding: "2rem 1rem",
353
+ textAlign: "center",
354
+ color: "var(--frost-text-secondary, #6b7280)"
355
+ }
356
+ };
357
+ var SPINNER_KEYFRAMES = `
358
+ @keyframes frost-spin {
359
+ from { transform: rotate(0deg); }
360
+ to { transform: rotate(360deg); }
361
+ }
362
+ `;
363
+ var stylesInjected = false;
364
+ function injectStyles() {
365
+ if (stylesInjected || typeof document === "undefined") return;
366
+ const style = document.createElement("style");
367
+ style.textContent = SPINNER_KEYFRAMES;
368
+ document.head.appendChild(style);
369
+ stylesInjected = true;
370
+ }
371
+ function WalletModal({
372
+ open,
373
+ onClose,
374
+ onConnect,
375
+ onError,
376
+ title = "Connect Wallet",
377
+ className,
378
+ contentClassName,
379
+ style,
380
+ renderWallet
381
+ }) {
382
+ const wallets = useWallets();
383
+ const status = useConnectionStatus();
384
+ const {
385
+ mutate: connect2,
386
+ variables: connectingWallet,
387
+ isPending
388
+ } = useConnectWallet({
389
+ onSuccess: (result) => {
390
+ onConnect?.(result.walletName, result.accountAddress);
391
+ },
392
+ onError: (error) => {
393
+ onError?.(error, connectingWallet?.walletName ?? "unknown");
394
+ }
395
+ });
396
+ const modalRef = react.useRef(null);
397
+ const previousActiveElement = react.useRef(null);
398
+ const wasOpen = react.useRef(false);
399
+ react.useEffect(() => {
400
+ injectStyles();
401
+ }, []);
402
+ react.useEffect(() => {
403
+ if (open && !wasOpen.current) {
404
+ previousActiveElement.current = document.activeElement;
405
+ modalRef.current?.focus();
406
+ } else if (!open && wasOpen.current) {
407
+ previousActiveElement.current?.focus();
408
+ }
409
+ wasOpen.current = open;
410
+ }, [open]);
411
+ react.useEffect(() => {
412
+ if (!open) return;
413
+ const handleKeyDown = (e) => {
414
+ if (e.key === "Escape") {
415
+ onClose();
416
+ return;
417
+ }
418
+ if (e.key === "Tab" && modalRef.current) {
419
+ const focusableElements = modalRef.current.querySelectorAll(
420
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
421
+ );
422
+ const firstElement = focusableElements[0];
423
+ const lastElement = focusableElements[focusableElements.length - 1];
424
+ if (e.shiftKey && document.activeElement === firstElement) {
425
+ e.preventDefault();
426
+ lastElement?.focus();
427
+ } else if (!e.shiftKey && document.activeElement === lastElement) {
428
+ e.preventDefault();
429
+ firstElement?.focus();
430
+ }
431
+ }
432
+ };
433
+ document.addEventListener("keydown", handleKeyDown);
434
+ const originalOverflow = document.body.style.overflow;
435
+ document.body.style.overflow = "hidden";
436
+ return () => {
437
+ document.removeEventListener("keydown", handleKeyDown);
438
+ document.body.style.overflow = originalOverflow;
439
+ };
440
+ }, [open, onClose]);
441
+ const handleOverlayClick = react.useCallback(
442
+ (e) => {
443
+ if (e.target === e.currentTarget) {
444
+ onClose();
445
+ }
446
+ },
447
+ [onClose]
448
+ );
449
+ const handleWalletClick = react.useCallback(
450
+ (walletName) => {
451
+ connect2({ walletName });
452
+ },
453
+ [connect2]
454
+ );
455
+ const handleWalletKeyDown = react.useCallback(
456
+ (e, walletName) => {
457
+ if (e.key === "Enter" || e.key === " ") {
458
+ e.preventDefault();
459
+ connect2({ walletName });
460
+ }
461
+ },
462
+ [connect2]
463
+ );
464
+ if (!open) return null;
465
+ const isConnecting = status === "connecting" || isPending;
466
+ return (
467
+ // biome-ignore lint/a11y/useKeyWithClickEvents: <modal>
468
+ /* @__PURE__ */ jsxRuntime.jsx(
469
+ "div",
470
+ {
471
+ role: "dialog",
472
+ "aria-modal": "true",
473
+ "aria-labelledby": "frost-modal-title",
474
+ className,
475
+ style: { ...defaultStyles.overlay, ...style },
476
+ onClick: handleOverlayClick,
477
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
478
+ "div",
479
+ {
480
+ ref: modalRef,
481
+ className: contentClassName,
482
+ style: defaultStyles.content,
483
+ tabIndex: -1,
484
+ "data-frost": "modal-content",
485
+ children: [
486
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: defaultStyles.header, "data-frost": "modal-header", children: [
487
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "frost-modal-title", style: defaultStyles.title, children: title }),
488
+ /* @__PURE__ */ jsxRuntime.jsx(
489
+ "button",
490
+ {
491
+ type: "button",
492
+ onClick: onClose,
493
+ style: defaultStyles.closeButton,
494
+ "aria-label": "Close modal",
495
+ "data-frost": "modal-close",
496
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {})
497
+ }
498
+ )
499
+ ] }),
500
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { style: defaultStyles.walletList, "data-frost": "wallet-list", children: wallets.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("li", { style: defaultStyles.emptyState, "data-frost": "empty-state", children: [
501
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "No wallets detected." }),
502
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { marginTop: "0.5rem", fontSize: "0.875rem" }, children: "Please install a Rialo-compatible wallet extension." })
503
+ ] }) : wallets.map((wallet) => {
504
+ const isThisConnecting = isConnecting && connectingWallet?.walletName === wallet.name;
505
+ if (renderWallet) {
506
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: renderWallet(wallet, () => handleWalletClick(wallet.name), isThisConnecting) }, wallet.name);
507
+ }
508
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
509
+ "button",
510
+ {
511
+ type: "button",
512
+ onClick: () => handleWalletClick(wallet.name),
513
+ onKeyDown: (e) => handleWalletKeyDown(e, wallet.name),
514
+ disabled: isConnecting,
515
+ style: {
516
+ ...defaultStyles.walletItem,
517
+ opacity: isConnecting && !isThisConnecting ? 0.5 : 1,
518
+ cursor: isConnecting ? "not-allowed" : "pointer"
519
+ },
520
+ "data-frost": "wallet-item",
521
+ "data-connecting": isThisConnecting ? "true" : void 0,
522
+ onMouseEnter: (e) => {
523
+ if (!isConnecting) {
524
+ e.currentTarget.style.backgroundColor = "var(--frost-hover-bg, #f3f4f6)";
525
+ }
526
+ },
527
+ onMouseLeave: (e) => {
528
+ e.currentTarget.style.backgroundColor = "transparent";
529
+ },
530
+ children: [
531
+ wallet.icon ? /* @__PURE__ */ jsxRuntime.jsx(
532
+ "img",
533
+ {
534
+ src: wallet.icon,
535
+ alt: `${wallet.name} icon`,
536
+ style: defaultStyles.walletIcon,
537
+ "data-frost": "wallet-icon"
538
+ }
539
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
540
+ "div",
541
+ {
542
+ style: {
543
+ ...defaultStyles.walletIcon,
544
+ display: "flex",
545
+ alignItems: "center",
546
+ justifyContent: "center",
547
+ color: "var(--frost-text-secondary, #6b7280)"
548
+ },
549
+ "data-frost": "wallet-icon-placeholder",
550
+ children: /* @__PURE__ */ jsxRuntime.jsx(WalletPlaceholderIcon, {})
551
+ }
552
+ ),
553
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: defaultStyles.walletInfo, children: [
554
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: defaultStyles.walletName, children: wallet.name }),
555
+ wallet.installedVersion && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: defaultStyles.walletStatus, children: [
556
+ "v",
557
+ wallet.installedVersion
558
+ ] })
559
+ ] }),
560
+ isThisConnecting && /* @__PURE__ */ jsxRuntime.jsx("div", { style: defaultStyles.spinner, "data-frost": "spinner" })
561
+ ]
562
+ }
563
+ ) }, wallet.name);
564
+ }) })
565
+ ]
566
+ }
567
+ )
568
+ }
569
+ )
570
+ );
571
+ }
572
+ var defaultStyles2 = {
573
+ container: {
574
+ position: "relative",
575
+ display: "inline-block"
576
+ },
577
+ button: {
578
+ display: "inline-flex",
579
+ alignItems: "center",
580
+ justifyContent: "center",
581
+ gap: "0.5rem",
582
+ padding: "0.625rem 1rem",
583
+ fontSize: "0.9375rem",
584
+ fontWeight: 500,
585
+ color: "var(--frost-button-text, #ffffff)",
586
+ backgroundColor: "var(--frost-primary, #3b82f6)",
587
+ border: "none",
588
+ borderRadius: "var(--frost-border-radius, 8px)",
589
+ cursor: "pointer",
590
+ transition: "all 0.15s ease",
591
+ minWidth: "140px",
592
+ fontFamily: "inherit"
593
+ },
594
+ buttonDisabled: {
595
+ opacity: 0.6,
596
+ cursor: "not-allowed"
597
+ },
598
+ buttonConnected: {
599
+ backgroundColor: "var(--frost-button-connected-bg, #f3f4f6)",
600
+ color: "var(--frost-button-connected-text, #111827)"
601
+ },
602
+ walletIcon: {
603
+ width: 20,
604
+ height: 20,
605
+ borderRadius: 4,
606
+ objectFit: "contain"
607
+ },
608
+ dropdown: {
609
+ position: "absolute",
610
+ top: "calc(100% + 4px)",
611
+ right: 0,
612
+ minWidth: "100%",
613
+ backgroundColor: "var(--frost-dropdown-bg, #ffffff)",
614
+ borderRadius: "var(--frost-border-radius, 8px)",
615
+ boxShadow: "var(--frost-shadow, 0 4px 16px rgba(0, 0, 0, 0.12))",
616
+ border: "1px solid var(--frost-border-color, #e5e7eb)",
617
+ overflow: "hidden",
618
+ zIndex: "var(--frost-z-index, 9999)"
619
+ },
620
+ dropdownItem: {
621
+ display: "flex",
622
+ alignItems: "center",
623
+ gap: "0.5rem",
624
+ width: "100%",
625
+ padding: "0.75rem 1rem",
626
+ fontSize: "0.875rem",
627
+ color: "var(--frost-text-color, #111827)",
628
+ backgroundColor: "transparent",
629
+ border: "none",
630
+ cursor: "pointer",
631
+ textAlign: "left",
632
+ fontFamily: "inherit",
633
+ transition: "background-color 0.15s ease"
634
+ },
635
+ dropdownDivider: {
636
+ height: 1,
637
+ backgroundColor: "var(--frost-border-color, #e5e7eb)",
638
+ margin: 0
639
+ },
640
+ addressDisplay: {
641
+ padding: "0.75rem 1rem",
642
+ fontSize: "0.75rem",
643
+ color: "var(--frost-text-secondary, #6b7280)",
644
+ fontFamily: "monospace",
645
+ wordBreak: "break-all"
646
+ },
647
+ spinner: {
648
+ width: 16,
649
+ height: 16,
650
+ border: "2px solid rgba(255, 255, 255, 0.3)",
651
+ borderTopColor: "#ffffff",
652
+ borderRadius: "50%",
653
+ animation: "frost-spin 0.8s linear infinite"
654
+ }
655
+ };
656
+ function truncateAddress(address, startChars = 6, endChars = 4) {
657
+ if (address.length <= startChars + endChars) return address;
658
+ return `${address.slice(0, startChars)}...${address.slice(-endChars)}`;
659
+ }
660
+ function ConnectButton({
661
+ label = "Connect Wallet",
662
+ connectingLabel = "Connecting...",
663
+ connectedLabel,
664
+ onConnect,
665
+ onDisconnect,
666
+ onError,
667
+ className,
668
+ style,
669
+ disabled = false,
670
+ modalTitle,
671
+ showDropdown = true,
672
+ dropdownClassName
673
+ }) {
674
+ const [modalOpen, setModalOpen] = react.useState(false);
675
+ const [dropdownOpen, setDropdownOpen] = react.useState(false);
676
+ const [copied, setCopied] = react.useState(false);
677
+ const containerRef = react.useRef(null);
678
+ const copyTimeoutRef = react.useRef(null);
679
+ const status = useConnectionStatus();
680
+ const account = useActiveAccount();
681
+ const wallet = useActiveWallet();
682
+ const { mutate: disconnect2 } = useDisconnectWallet({
683
+ onSuccess: () => {
684
+ setDropdownOpen(false);
685
+ onDisconnect?.();
686
+ }
687
+ });
688
+ const isConnected = status === "connected" && account;
689
+ const isConnecting = status === "connecting" || status === "reconnecting";
690
+ const handlersRef = react.useRef(null);
691
+ const removeListeners = react.useCallback(() => {
692
+ if (handlersRef.current) {
693
+ document.removeEventListener("mousedown", handlersRef.current.clickOutside);
694
+ document.removeEventListener("keydown", handlersRef.current.escape);
695
+ }
696
+ }, []);
697
+ const addListeners = react.useCallback(() => {
698
+ if (handlersRef.current) {
699
+ document.addEventListener("mousedown", handlersRef.current.clickOutside);
700
+ document.addEventListener("keydown", handlersRef.current.escape);
701
+ }
702
+ }, []);
703
+ if (!handlersRef.current) {
704
+ handlersRef.current = {
705
+ clickOutside: (e) => {
706
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
707
+ setDropdownOpen(false);
708
+ removeListeners();
709
+ }
710
+ },
711
+ escape: (e) => {
712
+ if (e.key === "Escape") {
713
+ setDropdownOpen(false);
714
+ removeListeners();
715
+ }
716
+ }
717
+ };
718
+ }
719
+ const setDropdownOpenWithListeners = react.useCallback(
720
+ (open) => {
721
+ setDropdownOpen(open);
722
+ if (open) {
723
+ addListeners();
724
+ } else {
725
+ removeListeners();
726
+ }
727
+ },
728
+ [addListeners, removeListeners]
729
+ );
730
+ react.useEffect(() => {
731
+ return () => {
732
+ removeListeners();
733
+ if (copyTimeoutRef.current) {
734
+ clearTimeout(copyTimeoutRef.current);
735
+ }
736
+ };
737
+ }, [removeListeners]);
738
+ const handleButtonClick = react.useCallback(() => {
739
+ if (isConnected) {
740
+ if (showDropdown) {
741
+ setDropdownOpenWithListeners(!dropdownOpen);
742
+ } else {
743
+ disconnect2();
744
+ }
745
+ } else if (!isConnecting) {
746
+ setModalOpen(true);
747
+ }
748
+ }, [
749
+ isConnected,
750
+ isConnecting,
751
+ showDropdown,
752
+ disconnect2,
753
+ setDropdownOpenWithListeners,
754
+ dropdownOpen
755
+ ]);
756
+ const handleCopyAddress = react.useCallback(async () => {
757
+ if (!account?.address) return;
758
+ try {
759
+ await navigator.clipboard.writeText(account.address);
760
+ setCopied(true);
761
+ if (copyTimeoutRef.current) {
762
+ clearTimeout(copyTimeoutRef.current);
763
+ }
764
+ copyTimeoutRef.current = setTimeout(() => setCopied(false), 2e3);
765
+ } catch (err) {
766
+ console.error("Failed to copy address:", err);
767
+ }
768
+ }, [account?.address]);
769
+ const handleModalConnect = react.useCallback(
770
+ (walletName, address) => {
771
+ setModalOpen(false);
772
+ onConnect?.(walletName, address);
773
+ },
774
+ [onConnect]
775
+ );
776
+ const getButtonLabel = () => {
777
+ if (isConnecting) return connectingLabel;
778
+ if (isConnected && account) {
779
+ if (typeof connectedLabel === "function") {
780
+ return connectedLabel(account.address, wallet?.name ?? "");
781
+ }
782
+ if (typeof connectedLabel === "string") return connectedLabel;
783
+ return truncateAddress(account.address);
784
+ }
785
+ return label;
786
+ };
787
+ const buttonStyles = {
788
+ ...defaultStyles2.button,
789
+ ...isConnected ? defaultStyles2.buttonConnected : {},
790
+ ...disabled || isConnecting ? defaultStyles2.buttonDisabled : {},
791
+ ...style
792
+ };
793
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: defaultStyles2.container, "data-frost": "connect-button-container", children: [
794
+ /* @__PURE__ */ jsxRuntime.jsxs(
795
+ "button",
796
+ {
797
+ type: "button",
798
+ onClick: handleButtonClick,
799
+ disabled: disabled || isConnecting,
800
+ className,
801
+ style: buttonStyles,
802
+ "data-frost": "connect-button",
803
+ "data-connected": isConnected ? "true" : void 0,
804
+ "data-connecting": isConnecting ? "true" : void 0,
805
+ "aria-expanded": dropdownOpen,
806
+ "aria-haspopup": isConnected && showDropdown ? "menu" : void 0,
807
+ onMouseEnter: (e) => {
808
+ if (!disabled && !isConnecting) e.currentTarget.style.opacity = "0.9";
809
+ },
810
+ onMouseLeave: (e) => {
811
+ e.currentTarget.style.opacity = "1";
812
+ },
813
+ children: [
814
+ isConnecting && /* @__PURE__ */ jsxRuntime.jsx("div", { style: defaultStyles2.spinner, "data-frost": "spinner" }),
815
+ isConnected && wallet?.icon && /* @__PURE__ */ jsxRuntime.jsx(
816
+ "img",
817
+ {
818
+ src: wallet.icon,
819
+ alt: wallet.name,
820
+ style: defaultStyles2.walletIcon,
821
+ "data-frost": "wallet-icon"
822
+ }
823
+ ),
824
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: getButtonLabel() }),
825
+ isConnected && showDropdown && /* @__PURE__ */ jsxRuntime.jsx(ChevronIcon, { direction: dropdownOpen ? "up" : "down" })
826
+ ]
827
+ }
828
+ ),
829
+ isConnected && showDropdown && dropdownOpen && /* @__PURE__ */ jsxRuntime.jsxs(
830
+ "div",
831
+ {
832
+ role: "menu",
833
+ className: dropdownClassName,
834
+ style: defaultStyles2.dropdown,
835
+ "data-frost": "dropdown",
836
+ children: [
837
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: defaultStyles2.addressDisplay, "data-frost": "address-display", children: account.address }),
838
+ /* @__PURE__ */ jsxRuntime.jsx("hr", { style: defaultStyles2.dropdownDivider }),
839
+ /* @__PURE__ */ jsxRuntime.jsxs(
840
+ "button",
841
+ {
842
+ type: "button",
843
+ role: "menuitem",
844
+ onClick: handleCopyAddress,
845
+ style: defaultStyles2.dropdownItem,
846
+ "data-frost": "dropdown-item",
847
+ onMouseEnter: (e) => {
848
+ e.currentTarget.style.backgroundColor = "var(--frost-hover-bg, #f3f4f6)";
849
+ },
850
+ onMouseLeave: (e) => {
851
+ e.currentTarget.style.backgroundColor = "transparent";
852
+ },
853
+ children: [
854
+ /* @__PURE__ */ jsxRuntime.jsx(CopyIcon, {}),
855
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: copied ? "Copied!" : "Copy Address" })
856
+ ]
857
+ }
858
+ ),
859
+ /* @__PURE__ */ jsxRuntime.jsxs(
860
+ "button",
861
+ {
862
+ type: "button",
863
+ role: "menuitem",
864
+ onClick: () => disconnect2(),
865
+ style: { ...defaultStyles2.dropdownItem, color: "var(--frost-danger, #ef4444)" },
866
+ "data-frost": "dropdown-item",
867
+ "data-action": "disconnect",
868
+ onMouseEnter: (e) => {
869
+ e.currentTarget.style.backgroundColor = "var(--frost-hover-bg, #f3f4f6)";
870
+ },
871
+ onMouseLeave: (e) => {
872
+ e.currentTarget.style.backgroundColor = "transparent";
873
+ },
874
+ children: [
875
+ /* @__PURE__ */ jsxRuntime.jsx(DisconnectIcon, {}),
876
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Disconnect" })
877
+ ]
878
+ }
879
+ )
880
+ ]
881
+ }
882
+ ),
883
+ /* @__PURE__ */ jsxRuntime.jsx(
884
+ WalletModal,
885
+ {
886
+ open: modalOpen,
887
+ onClose: () => setModalOpen(false),
888
+ onConnect: handleModalConnect,
889
+ onError,
890
+ title: modalTitle
891
+ }
892
+ )
893
+ ] });
894
+ }
895
+ var selectAccountsMap = (state) => state.accounts;
896
+ function useAccounts() {
897
+ const config = useFrostConfig();
898
+ const accountsMap = reactStore.useStore(config.store, selectAccountsMap);
899
+ return react.useMemo(() => Array.from(accountsMap.values()), [accountsMap]);
900
+ }
901
+ var selectChainId = (state) => state.chainId;
902
+ function useSwitchChain() {
903
+ const config = useFrostConfig();
904
+ const switchChain = react.useCallback(
905
+ () => (clientConfig) => {
906
+ config.switchChain(clientConfig);
907
+ },
908
+ [config]
909
+ );
910
+ return switchChain;
911
+ }
912
+ function useChainId() {
913
+ const config = useFrostConfig();
914
+ return reactStore.useStore(config.store, selectChainId);
915
+ }
916
+
917
+ // src/hooks/useClient.ts
918
+ function useClient() {
919
+ const config = useFrostConfig();
920
+ return config.client;
921
+ }
922
+ var selectAccountAddress = (state) => state.accountAddress;
923
+ function formatBalance(balance, decimals) {
924
+ const divisor = BigInt(10 ** decimals);
925
+ const integerPart = balance / divisor;
926
+ const fractionalPart = balance % divisor;
927
+ const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
928
+ const trimmed = fractionalStr.replace(/0+$/, "");
929
+ if (!trimmed) {
930
+ return integerPart.toString();
931
+ }
932
+ return `${integerPart}.${trimmed}`;
933
+ }
934
+ function useNativeBalance(options = {}) {
935
+ const { decimals = 9 } = options;
936
+ const config = useFrostConfig();
937
+ const activeAddress = reactStore.useStore(config.store, selectAccountAddress);
938
+ const address = options.address ?? activeAddress;
939
+ const hasAddress = !!address;
940
+ const query = reactQuery.useQuery({
941
+ queryKey: ["frost", "nativeBalance", address, config.getChainId()],
942
+ queryFn: () => {
943
+ if (!address) throw new Error("No address provided");
944
+ return config.client.getBalance(tsCdk.PublicKey.fromString(address));
945
+ },
946
+ enabled: hasAddress
947
+ });
948
+ return {
949
+ balance: query.data,
950
+ formatted: query.data !== void 0 ? formatBalance(query.data, decimals) : void 0,
951
+ isLoading: query.isLoading,
952
+ isFetching: query.isFetching,
953
+ isError: query.isError,
954
+ error: query.error,
955
+ refetch: () => {
956
+ void query.refetch();
957
+ }
958
+ };
959
+ }
960
+ function useSignMessage(options) {
961
+ const config = useFrostConfig();
962
+ return reactQuery.useMutation({
963
+ mutationKey: ["frost", "signMessage"],
964
+ mutationFn: (msgOptions) => frostCore.signMessage(config, msgOptions),
965
+ onSuccess: options?.onSuccess,
966
+ onError: options?.onError
967
+ });
968
+ }
969
+ function useSignTransaction(options) {
970
+ const config = useFrostConfig();
971
+ return reactQuery.useMutation({
972
+ mutationKey: ["frost", "signTransaction"],
973
+ mutationFn: (txOptions) => frostCore.signTransaction(config, txOptions),
974
+ onSuccess: options?.onSuccess,
975
+ onError: options?.onError
976
+ });
977
+ }
978
+ function useSendTransaction(options) {
979
+ const config = useFrostConfig();
980
+ return reactQuery.useMutation({
981
+ mutationKey: ["frost", "sendTransaction"],
982
+ mutationFn: (txOptions) => frostCore.sendTransaction(config, txOptions),
983
+ onSuccess: options?.onSuccess,
984
+ onError: options?.onError
985
+ });
986
+ }
987
+ function useSignAndSendTransaction(options) {
988
+ const config = useFrostConfig();
989
+ return reactQuery.useMutation({
990
+ mutationKey: ["frost", "signAndSendTransaction"],
991
+ mutationFn: (txOptions) => frostCore.signAndSendTransaction(config, txOptions),
992
+ onSuccess: options?.onSuccess,
993
+ onError: options?.onError
994
+ });
995
+ }
996
+
997
+ exports.ConnectButton = ConnectButton;
998
+ exports.FrostProvider = FrostProvider;
999
+ exports.WalletModal = WalletModal;
1000
+ exports.useAccounts = useAccounts;
1001
+ exports.useActiveAccount = useActiveAccount;
1002
+ exports.useActiveWallet = useActiveWallet;
1003
+ exports.useChainId = useChainId;
1004
+ exports.useClient = useClient;
1005
+ exports.useConnectWallet = useConnectWallet;
1006
+ exports.useConnectionStatus = useConnectionStatus;
1007
+ exports.useDisconnectWallet = useDisconnectWallet;
1008
+ exports.useFrostConfig = useFrostConfig;
1009
+ exports.useIsConnected = useIsConnected;
1010
+ exports.useNativeBalance = useNativeBalance;
1011
+ exports.useSendTransaction = useSendTransaction;
1012
+ exports.useSignAndSendTransaction = useSignAndSendTransaction;
1013
+ exports.useSignMessage = useSignMessage;
1014
+ exports.useSignTransaction = useSignTransaction;
1015
+ exports.useSwitchChain = useSwitchChain;
1016
+ exports.useWallets = useWallets;
1017
+ exports.useWalletsReady = useWalletsReady;
1018
+ Object.keys(frostCore).forEach(function (k) {
1019
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
1020
+ enumerable: true,
1021
+ get: function () { return frostCore[k]; }
1022
+ });
1023
+ });
1024
+ //# sourceMappingURL=index.cjs.map
1025
+ //# sourceMappingURL=index.cjs.map