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