@phantom/react-sdk 1.0.0-beta.21 → 1.0.0-beta.22

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.mjs CHANGED
@@ -1,31 +1,488 @@
1
1
  // src/PhantomProvider.tsx
2
- import { createContext, useContext, useState, useEffect, useMemo } from "react";
2
+ import { useState as useState7, useEffect as useEffect4, useMemo as useMemo3 } from "react";
3
3
  import { BrowserSDK } from "@phantom/browser-sdk";
4
- import { jsx } from "react/jsx-runtime";
4
+ import { mergeTheme, darkTheme, ThemeProvider } from "@phantom/wallet-sdk-ui";
5
+
6
+ // src/PhantomContext.ts
7
+ import { createContext, useContext } from "react";
5
8
  var PhantomContext = createContext(void 0);
6
- function PhantomProvider({ children, config, debugConfig }) {
7
- const memoizedConfig = useMemo(() => config, [config]);
8
- const [sdk, setSdk] = useState(null);
9
- const [isClient, setIsClient] = useState(false);
10
- const [isConnected, setIsConnected] = useState(false);
11
- const [isConnecting, setIsConnecting] = useState(false);
12
- const [connectError, setConnectError] = useState(null);
13
- const [addresses, setAddresses] = useState([]);
14
- const [currentProviderType, setCurrentProviderType] = useState(
15
- memoizedConfig.providerType || null
9
+ function usePhantom() {
10
+ const context = useContext(PhantomContext);
11
+ if (!context) {
12
+ throw new Error("usePhantom must be used within a PhantomProvider");
13
+ }
14
+ return context;
15
+ }
16
+
17
+ // src/ModalProvider.tsx
18
+ import { useState as useState6, useCallback as useCallback4, useMemo as useMemo2 } from "react";
19
+
20
+ // src/ModalContext.ts
21
+ import { createContext as createContext2, useContext as useContext2 } from "react";
22
+ var ModalContext = createContext2(void 0);
23
+ function useModal() {
24
+ const context = useContext2(ModalContext);
25
+ if (!context) {
26
+ throw new Error("useModal must be used within a ModalProvider");
27
+ }
28
+ return {
29
+ open: context.openModal,
30
+ close: context.closeModal,
31
+ isOpened: context.isModalOpen
32
+ };
33
+ }
34
+
35
+ // src/ModalProvider.tsx
36
+ import { isMobileDevice as isMobileDevice2 } from "@phantom/browser-sdk";
37
+ import { Modal } from "@phantom/wallet-sdk-ui";
38
+
39
+ // src/components/ConnectModalContent.tsx
40
+ import { useState as useState3, useCallback as useCallback2, useMemo } from "react";
41
+ import { isMobileDevice, getDeeplinkToPhantom } from "@phantom/browser-sdk";
42
+ import { Button, LoginWithPhantomButton, Icon, BoundedIcon, Text, hexToRgba, useTheme } from "@phantom/wallet-sdk-ui";
43
+
44
+ // src/hooks/useIsExtensionInstalled.ts
45
+ import * as React from "react";
46
+ import { waitForPhantomExtension } from "@phantom/browser-sdk";
47
+ function useIsExtensionInstalled() {
48
+ const [isLoading, setIsLoading] = React.useState(true);
49
+ const [isInstalled, setIsInstalled] = React.useState(false);
50
+ React.useEffect(() => {
51
+ let isMounted = true;
52
+ const checkExtension = async () => {
53
+ try {
54
+ setIsLoading(true);
55
+ const result = await waitForPhantomExtension(3e3);
56
+ if (isMounted) {
57
+ setIsInstalled(result);
58
+ }
59
+ } catch (error) {
60
+ if (isMounted) {
61
+ setIsInstalled(false);
62
+ }
63
+ } finally {
64
+ if (isMounted) {
65
+ setIsLoading(false);
66
+ }
67
+ }
68
+ };
69
+ checkExtension();
70
+ return () => {
71
+ isMounted = false;
72
+ };
73
+ }, []);
74
+ return { isLoading, isInstalled };
75
+ }
76
+
77
+ // src/hooks/useIsPhantomLoginAvailable.ts
78
+ import * as React2 from "react";
79
+ import { isPhantomLoginAvailable } from "@phantom/browser-sdk";
80
+ function useIsPhantomLoginAvailable() {
81
+ const [isLoading, setIsLoading] = React2.useState(true);
82
+ const [isAvailable, setIsAvailable] = React2.useState(false);
83
+ React2.useEffect(() => {
84
+ let isMounted = true;
85
+ const checkPhantomLogin = async () => {
86
+ try {
87
+ setIsLoading(true);
88
+ const result = await isPhantomLoginAvailable(3e3);
89
+ if (isMounted) {
90
+ setIsAvailable(result);
91
+ }
92
+ } catch (error) {
93
+ if (isMounted) {
94
+ setIsAvailable(false);
95
+ }
96
+ } finally {
97
+ if (isMounted) {
98
+ setIsLoading(false);
99
+ }
100
+ }
101
+ };
102
+ checkPhantomLogin();
103
+ return () => {
104
+ isMounted = false;
105
+ };
106
+ }, []);
107
+ return { isLoading, isAvailable };
108
+ }
109
+
110
+ // src/hooks/useConnect.ts
111
+ import { useCallback } from "react";
112
+ function useConnect() {
113
+ const { sdk, isConnecting, isLoading, connectError } = usePhantom();
114
+ const connect = useCallback(
115
+ async (options) => {
116
+ if (!sdk) {
117
+ throw new Error("SDK not initialized");
118
+ }
119
+ try {
120
+ const result = await sdk.connect(options);
121
+ return result;
122
+ } catch (err) {
123
+ console.error("Error connecting to Phantom:", err);
124
+ throw err;
125
+ }
126
+ },
127
+ [sdk]
128
+ );
129
+ return {
130
+ connect,
131
+ isConnecting,
132
+ isLoading,
133
+ error: connectError
134
+ };
135
+ }
136
+
137
+ // src/components/ConnectModalContent.tsx
138
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
139
+ function ConnectModalContent({ appIcon, appName = "App Name", onClose }) {
140
+ const theme = useTheme();
141
+ const { isLoading, allowedProviders } = usePhantom();
142
+ const baseConnect = useConnect();
143
+ const isExtensionInstalled = useIsExtensionInstalled();
144
+ const isPhantomLoginAvailable2 = useIsPhantomLoginAvailable();
145
+ const isMobile = useMemo(() => isMobileDevice(), []);
146
+ const [isConnecting, setIsConnecting] = useState3(false);
147
+ const [error, setError] = useState3(null);
148
+ const [providerType, setProviderType] = useState3(null);
149
+ const showDivider = !(allowedProviders.length === 1 && allowedProviders.includes("injected"));
150
+ const connectWithAuthProvider = useCallback2(
151
+ async (provider) => {
152
+ try {
153
+ setIsConnecting(true);
154
+ setError(null);
155
+ setProviderType(provider);
156
+ await baseConnect.connect({ provider });
157
+ onClose();
158
+ } catch (err) {
159
+ const error2 = err instanceof Error ? err : new Error(String(err));
160
+ setError(error2);
161
+ } finally {
162
+ setIsConnecting(false);
163
+ setProviderType(null);
164
+ }
165
+ },
166
+ [baseConnect, onClose]
167
+ );
168
+ const connectWithDeeplink = useCallback2(() => {
169
+ try {
170
+ setIsConnecting(true);
171
+ setError(null);
172
+ setProviderType("deeplink");
173
+ const deeplinkUrl = getDeeplinkToPhantom();
174
+ window.location.href = deeplinkUrl;
175
+ onClose();
176
+ } catch (err) {
177
+ const error2 = err instanceof Error ? err : new Error(String(err));
178
+ setError(error2);
179
+ } finally {
180
+ setIsConnecting(false);
181
+ setProviderType(null);
182
+ }
183
+ }, [onClose]);
184
+ const appIconStyle = {
185
+ width: "56px",
186
+ height: "56px",
187
+ borderRadius: "50%",
188
+ display: "block",
189
+ objectFit: "cover"
190
+ };
191
+ const buttonContainerStyle = {
192
+ display: "flex",
193
+ flexDirection: "column",
194
+ alignItems: "center",
195
+ gap: "12px",
196
+ width: "100%"
197
+ };
198
+ const socialButtonRowStyle = {
199
+ display: "flex",
200
+ gap: "12px",
201
+ width: "100%"
202
+ };
203
+ const dividerStyle = {
204
+ display: "flex",
205
+ alignItems: "center",
206
+ width: "100%",
207
+ margin: "24px 0",
208
+ ...theme.typography.caption,
209
+ color: theme.secondary,
210
+ textTransform: "uppercase"
211
+ };
212
+ const dividerLineStyle = {
213
+ flex: 1,
214
+ height: "1px",
215
+ backgroundColor: hexToRgba(theme.secondary, 0.1)
216
+ };
217
+ const dividerTextStyle = {
218
+ padding: "0 12px"
219
+ };
220
+ const errorStyle = {
221
+ backgroundColor: "rgba(220, 53, 69, 0.1)",
222
+ color: "#ff6b6b",
223
+ border: "1px solid rgba(220, 53, 69, 0.3)",
224
+ borderRadius: theme.borderRadius,
225
+ padding: "12px",
226
+ width: "100%",
227
+ fontSize: "14px"
228
+ };
229
+ const loadingContainerStyle = {
230
+ display: "flex",
231
+ flexDirection: "column",
232
+ alignItems: "center",
233
+ justifyContent: "center",
234
+ padding: "24px",
235
+ gap: "12px"
236
+ };
237
+ const spinnerStyle = {
238
+ width: "40px",
239
+ height: "40px",
240
+ border: `3px solid ${theme.secondary}`,
241
+ borderTop: `3px solid ${theme.brand}`,
242
+ borderRadius: "50%",
243
+ animation: "spin 1s linear infinite"
244
+ };
245
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
246
+ /* @__PURE__ */ jsx("style", { children: `
247
+ @keyframes spin {
248
+ 0% { transform: rotate(0deg); }
249
+ 100% { transform: rotate(360deg); }
250
+ }
251
+ ` }),
252
+ appIcon && /* @__PURE__ */ jsx("img", { src: appIcon, alt: appName, style: appIconStyle }),
253
+ error && /* @__PURE__ */ jsx("div", { style: errorStyle, children: error.message }),
254
+ isLoading ? /* @__PURE__ */ jsxs("div", { style: loadingContainerStyle, children: [
255
+ /* @__PURE__ */ jsx("div", { style: spinnerStyle }),
256
+ /* @__PURE__ */ jsx(Text, { variant: "label", color: theme.secondary, children: "Loading..." })
257
+ ] }) : /* @__PURE__ */ jsxs("div", { style: buttonContainerStyle, children: [
258
+ isMobile && !isExtensionInstalled.isInstalled && /* @__PURE__ */ jsx(
259
+ Button,
260
+ {
261
+ onClick: connectWithDeeplink,
262
+ disabled: isConnecting,
263
+ isLoading: isConnecting && providerType === "deeplink",
264
+ fullWidth: true,
265
+ children: isConnecting && providerType === "deeplink" ? "Opening Phantom..." : "Open in Phantom App"
266
+ }
267
+ ),
268
+ !isMobile && allowedProviders.includes("phantom") && isPhantomLoginAvailable2.isAvailable && /* @__PURE__ */ jsx(
269
+ LoginWithPhantomButton,
270
+ {
271
+ onClick: () => connectWithAuthProvider("phantom"),
272
+ disabled: isConnecting,
273
+ isLoading: isConnecting && providerType === "phantom"
274
+ }
275
+ ),
276
+ (allowedProviders.includes("google") || allowedProviders.includes("apple")) && /* @__PURE__ */ jsxs("div", { style: socialButtonRowStyle, children: [
277
+ allowedProviders.includes("google") && /* @__PURE__ */ jsxs(
278
+ Button,
279
+ {
280
+ onClick: () => connectWithAuthProvider("google"),
281
+ disabled: isConnecting,
282
+ isLoading: isConnecting && providerType === "google",
283
+ fullWidth: true,
284
+ centered: allowedProviders.includes("apple"),
285
+ children: [
286
+ /* @__PURE__ */ jsx(Icon, { type: "google", size: 20 }),
287
+ !allowedProviders.includes("apple") && /* @__PURE__ */ jsx(Text, { variant: "captionBold", children: "Continue with Google" })
288
+ ]
289
+ }
290
+ ),
291
+ allowedProviders.includes("apple") && /* @__PURE__ */ jsxs(
292
+ Button,
293
+ {
294
+ onClick: () => connectWithAuthProvider("apple"),
295
+ disabled: isConnecting,
296
+ isLoading: isConnecting && providerType === "apple",
297
+ fullWidth: true,
298
+ centered: allowedProviders.includes("google"),
299
+ children: [
300
+ /* @__PURE__ */ jsx(Icon, { type: "apple", size: 20 }),
301
+ !allowedProviders.includes("google") && /* @__PURE__ */ jsx(Text, { variant: "captionBold", children: "Continue with Apple" })
302
+ ]
303
+ }
304
+ )
305
+ ] }),
306
+ !isMobile && allowedProviders.includes("injected") && isExtensionInstalled.isInstalled && /* @__PURE__ */ jsxs(Fragment, { children: [
307
+ showDivider && /* @__PURE__ */ jsxs("div", { style: dividerStyle, children: [
308
+ /* @__PURE__ */ jsx("div", { style: dividerLineStyle }),
309
+ /* @__PURE__ */ jsx("span", { style: dividerTextStyle, children: "OR" }),
310
+ /* @__PURE__ */ jsx("div", { style: dividerLineStyle })
311
+ ] }),
312
+ /* @__PURE__ */ jsx(
313
+ Button,
314
+ {
315
+ onClick: () => connectWithAuthProvider("injected"),
316
+ disabled: isConnecting,
317
+ isLoading: isConnecting && providerType === "injected",
318
+ fullWidth: true,
319
+ children: /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "8px" }, children: [
320
+ /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
321
+ /* @__PURE__ */ jsx(BoundedIcon, { type: "phantom", size: 20, background: "#AB9FF2", color: "white" }),
322
+ /* @__PURE__ */ jsx(Text, { variant: "captionBold", children: "Phantom" })
323
+ ] }),
324
+ /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
325
+ /* @__PURE__ */ jsx(Text, { variant: "label", color: theme.secondary, children: "Detected" }),
326
+ /* @__PURE__ */ jsx(Icon, { type: "chevron-right", size: 16 })
327
+ ] })
328
+ ] })
329
+ }
330
+ )
331
+ ] })
332
+ ] })
333
+ ] });
334
+ }
335
+
336
+ // src/components/ConnectedModalContent.tsx
337
+ import { useState as useState5, useEffect as useEffect3 } from "react";
338
+ import { Button as Button2, Text as Text2, useTheme as useTheme2 } from "@phantom/wallet-sdk-ui";
339
+
340
+ // src/hooks/useDisconnect.ts
341
+ import { useCallback as useCallback3, useState as useState4 } from "react";
342
+ function useDisconnect() {
343
+ const { sdk } = usePhantom();
344
+ const [isDisconnecting, setIsDisconnecting] = useState4(false);
345
+ const [error, setError] = useState4(null);
346
+ const disconnect = useCallback3(async () => {
347
+ if (!sdk) {
348
+ throw new Error("SDK not initialized");
349
+ }
350
+ setIsDisconnecting(true);
351
+ setError(null);
352
+ try {
353
+ await sdk.disconnect();
354
+ } catch (err) {
355
+ const error2 = err instanceof Error ? err : new Error(String(err));
356
+ setError(error2);
357
+ throw err;
358
+ } finally {
359
+ setIsDisconnecting(false);
360
+ }
361
+ }, [sdk]);
362
+ return {
363
+ disconnect,
364
+ isDisconnecting,
365
+ error
366
+ };
367
+ }
368
+
369
+ // src/components/ConnectedModalContent.tsx
370
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
371
+ function ConnectedModalContent({ onClose }) {
372
+ const theme = useTheme2();
373
+ const { addresses } = usePhantom();
374
+ const { disconnect } = useDisconnect();
375
+ const [isDisconnecting, setIsDisconnecting] = useState5(false);
376
+ const [disconnectError, setDisconnectError] = useState5(null);
377
+ useEffect3(() => {
378
+ setDisconnectError(null);
379
+ }, []);
380
+ const handleDisconnect = async () => {
381
+ try {
382
+ setIsDisconnecting(true);
383
+ setDisconnectError(null);
384
+ await disconnect();
385
+ onClose();
386
+ } catch (err) {
387
+ const error = err instanceof Error ? err : new Error(String(err));
388
+ setDisconnectError(error);
389
+ } finally {
390
+ setIsDisconnecting(false);
391
+ }
392
+ };
393
+ const accountListStyle = {
394
+ display: "flex",
395
+ flexDirection: "column",
396
+ gap: "16px",
397
+ width: "100%"
398
+ };
399
+ const accountItemStyle = {
400
+ display: "flex",
401
+ flexDirection: "column",
402
+ gap: "8px",
403
+ width: "100%"
404
+ };
405
+ const addressTextStyle = {
406
+ fontFamily: "monospace",
407
+ wordBreak: "break-all"
408
+ };
409
+ const errorContainerStyle = {
410
+ padding: "12px",
411
+ backgroundColor: "rgba(220, 53, 69, 0.1)",
412
+ borderRadius: theme.borderRadius,
413
+ border: "1px solid rgba(220, 53, 69, 0.3)",
414
+ width: "100%"
415
+ };
416
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
417
+ addresses && addresses.length > 0 && /* @__PURE__ */ jsx2("div", { style: accountListStyle, children: addresses.map((account, index) => /* @__PURE__ */ jsxs2("div", { style: accountItemStyle, children: [
418
+ /* @__PURE__ */ jsx2(Text2, { variant: "label", color: theme.secondary, style: { textTransform: "uppercase" }, children: account.addressType }),
419
+ /* @__PURE__ */ jsx2("div", { style: addressTextStyle, children: /* @__PURE__ */ jsx2(Text2, { variant: "caption", children: account.address }) })
420
+ ] }, index)) }),
421
+ disconnectError && /* @__PURE__ */ jsx2("div", { style: errorContainerStyle, children: /* @__PURE__ */ jsx2(Text2, { variant: "caption", color: theme.error, children: "Failed to disconnect" }) }),
422
+ /* @__PURE__ */ jsx2(Button2, { onClick: handleDisconnect, disabled: isDisconnecting, isLoading: isDisconnecting, fullWidth: true, children: /* @__PURE__ */ jsx2(Text2, { variant: "captionBold", children: isDisconnecting ? "Disconnecting..." : "Disconnect" }) })
423
+ ] });
424
+ }
425
+
426
+ // src/ModalProvider.tsx
427
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
428
+ function ModalProvider({ children, appIcon, appName }) {
429
+ const { isConnected } = usePhantom();
430
+ const [isModalOpen, setIsModalOpen] = useState6(false);
431
+ const isMobile = useMemo2(() => isMobileDevice2(), []);
432
+ const openModal = useCallback4(() => {
433
+ setIsModalOpen(true);
434
+ }, []);
435
+ const closeModal = useCallback4(() => {
436
+ setIsModalOpen(false);
437
+ }, []);
438
+ const modalContextValue = useMemo2(
439
+ () => ({
440
+ isModalOpen,
441
+ openModal,
442
+ closeModal
443
+ }),
444
+ [isModalOpen, openModal, closeModal]
16
445
  );
17
- const [isPhantomAvailable, setIsPhantomAvailable] = useState(false);
18
- const [user, setUser] = useState(null);
19
- useEffect(() => {
446
+ return /* @__PURE__ */ jsxs3(ModalContext.Provider, { value: modalContextValue, children: [
447
+ children,
448
+ /* @__PURE__ */ jsx3(
449
+ Modal,
450
+ {
451
+ isVisible: isModalOpen,
452
+ onClose: closeModal,
453
+ appIcon,
454
+ appName,
455
+ isConnected,
456
+ isMobile,
457
+ children: isConnected ? /* @__PURE__ */ jsx3(ConnectedModalContent, { onClose: closeModal }) : /* @__PURE__ */ jsx3(ConnectModalContent, { appIcon, appName, onClose: closeModal })
458
+ }
459
+ )
460
+ ] });
461
+ }
462
+
463
+ // src/PhantomProvider.tsx
464
+ import { jsx as jsx4 } from "react/jsx-runtime";
465
+ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appName }) {
466
+ const memoizedConfig = useMemo3(() => config, [config]);
467
+ const resolvedTheme = useMemo3(() => mergeTheme(theme || darkTheme), [theme]);
468
+ const [sdk, setSdk] = useState7(null);
469
+ const [isClient, setIsClient] = useState7(false);
470
+ const [isConnected, setIsConnected] = useState7(false);
471
+ const [isConnecting, setIsConnecting] = useState7(false);
472
+ const [isLoading, setIsLoading] = useState7(true);
473
+ const [connectError, setConnectError] = useState7(null);
474
+ const [addresses, setAddresses] = useState7([]);
475
+ const [user, setUser] = useState7(null);
476
+ useEffect4(() => {
20
477
  setIsClient(true);
21
478
  }, []);
22
- useEffect(() => {
479
+ useEffect4(() => {
23
480
  if (!isClient)
24
481
  return;
25
482
  const sdkInstance = new BrowserSDK(memoizedConfig);
26
483
  setSdk(sdkInstance);
27
484
  }, [isClient, memoizedConfig]);
28
- useEffect(() => {
485
+ useEffect4(() => {
29
486
  if (!sdk)
30
487
  return;
31
488
  const handleConnectStart = () => {
@@ -37,7 +494,6 @@ function PhantomProvider({ children, config, debugConfig }) {
37
494
  setIsConnected(true);
38
495
  setIsConnecting(false);
39
496
  setUser(data);
40
- setCurrentProviderType(data.providerType || null);
41
497
  const addrs = await sdk.getAddresses();
42
498
  setAddresses(addrs);
43
499
  } catch (err) {
@@ -73,107 +529,51 @@ function PhantomProvider({ children, config, debugConfig }) {
73
529
  sdk.off("disconnect", handleDisconnect);
74
530
  };
75
531
  }, [sdk]);
76
- useEffect(() => {
532
+ useEffect4(() => {
77
533
  if (!debugConfig || !sdk)
78
534
  return;
79
535
  sdk.configureDebug(debugConfig);
80
536
  }, [sdk, debugConfig]);
81
- useEffect(() => {
537
+ useEffect4(() => {
82
538
  if (!isClient || !sdk)
83
539
  return;
84
540
  const initialize = async () => {
85
541
  try {
86
- const available = await BrowserSDK.isPhantomInstalled();
87
- setIsPhantomAvailable(available);
88
- } catch (err) {
89
- console.error("Error checking Phantom extension:", err);
90
- setIsPhantomAvailable(false);
91
- }
92
- if (memoizedConfig.autoConnect !== false) {
93
- sdk.autoConnect().catch(() => {
94
- });
542
+ await sdk.autoConnect();
543
+ } catch (error) {
544
+ console.error("Auto-connect error:", error);
95
545
  }
546
+ setIsLoading(false);
96
547
  };
97
548
  initialize();
98
- }, [sdk, memoizedConfig.autoConnect, isClient]);
99
- const value = useMemo(
549
+ }, [sdk, isClient]);
550
+ const value = useMemo3(
100
551
  () => ({
101
552
  sdk,
102
553
  isConnected,
103
554
  isConnecting,
555
+ isLoading,
104
556
  connectError,
105
557
  addresses,
106
- currentProviderType,
107
- isPhantomAvailable,
108
558
  isClient,
109
- user
559
+ user,
560
+ theme: resolvedTheme,
561
+ allowedProviders: memoizedConfig.providers
110
562
  }),
111
- [sdk, isConnected, isConnecting, connectError, addresses, currentProviderType, isPhantomAvailable, isClient, user]
112
- );
113
- return /* @__PURE__ */ jsx(PhantomContext.Provider, { value, children });
114
- }
115
- function usePhantom() {
116
- const context = useContext(PhantomContext);
117
- if (!context) {
118
- throw new Error("usePhantom must be used within a PhantomProvider");
119
- }
120
- return context;
121
- }
122
-
123
- // src/hooks/useConnect.ts
124
- import { useCallback } from "react";
125
- function useConnect() {
126
- const { sdk, isConnecting, connectError, currentProviderType, isPhantomAvailable } = usePhantom();
127
- const connect = useCallback(
128
- async (options) => {
129
- if (!sdk) {
130
- throw new Error("SDK not initialized");
131
- }
132
- try {
133
- const result = await sdk.connect(options);
134
- return result;
135
- } catch (err) {
136
- console.error("Error connecting to Phantom:", err);
137
- throw err;
138
- }
139
- },
140
- [sdk]
563
+ [
564
+ sdk,
565
+ isConnected,
566
+ isConnecting,
567
+ isLoading,
568
+ connectError,
569
+ addresses,
570
+ isClient,
571
+ user,
572
+ resolvedTheme,
573
+ memoizedConfig.providers
574
+ ]
141
575
  );
142
- return {
143
- connect,
144
- isConnecting,
145
- error: connectError,
146
- currentProviderType,
147
- isPhantomAvailable
148
- };
149
- }
150
-
151
- // src/hooks/useDisconnect.ts
152
- import { useCallback as useCallback2, useState as useState2 } from "react";
153
- function useDisconnect() {
154
- const { sdk } = usePhantom();
155
- const [isDisconnecting, setIsDisconnecting] = useState2(false);
156
- const [error, setError] = useState2(null);
157
- const disconnect = useCallback2(async () => {
158
- if (!sdk) {
159
- throw new Error("SDK not initialized");
160
- }
161
- setIsDisconnecting(true);
162
- setError(null);
163
- try {
164
- await sdk.disconnect();
165
- } catch (err) {
166
- setError(err);
167
- throw err;
168
- } finally {
169
- setIsDisconnecting(false);
170
- }
171
- }, [sdk]);
172
- return {
173
- disconnect,
174
- isDisconnecting,
175
- error
176
- };
576
+ return /* @__PURE__ */ jsx4(ThemeProvider, { theme: resolvedTheme, children: /* @__PURE__ */ jsx4(PhantomContext.Provider, { value, children: /* @__PURE__ */ jsx4(ModalProvider, { appIcon, appName, children }) }) });
177
577
  }
178
578
 
179
579
  // src/hooks/useAccounts.ts
@@ -182,82 +582,16 @@ function useAccounts() {
182
582
  return isConnected ? addresses : null;
183
583
  }
184
584
 
185
- // src/hooks/useIsExtensionInstalled.ts
186
- import * as React from "react";
187
- import { waitForPhantomExtension } from "@phantom/browser-sdk";
188
- function useIsExtensionInstalled() {
189
- const [isLoading, setIsLoading] = React.useState(true);
190
- const [isInstalled, setIsInstalled] = React.useState(false);
191
- React.useEffect(() => {
192
- let isMounted = true;
193
- const checkExtension = async () => {
194
- try {
195
- setIsLoading(true);
196
- const result = await waitForPhantomExtension(3e3);
197
- if (isMounted) {
198
- setIsInstalled(result);
199
- }
200
- } catch (error) {
201
- if (isMounted) {
202
- setIsInstalled(false);
203
- }
204
- } finally {
205
- if (isMounted) {
206
- setIsLoading(false);
207
- }
208
- }
209
- };
210
- checkExtension();
211
- return () => {
212
- isMounted = false;
213
- };
214
- }, []);
215
- return { isLoading, isInstalled };
216
- }
217
-
218
- // src/hooks/useIsPhantomLoginAvailable.ts
219
- import * as React2 from "react";
220
- import { isPhantomLoginAvailable } from "@phantom/browser-sdk";
221
- function useIsPhantomLoginAvailable() {
222
- const [isLoading, setIsLoading] = React2.useState(true);
223
- const [isAvailable, setIsAvailable] = React2.useState(false);
224
- React2.useEffect(() => {
225
- let isMounted = true;
226
- const checkPhantomLogin = async () => {
227
- try {
228
- setIsLoading(true);
229
- const result = await isPhantomLoginAvailable(3e3);
230
- if (isMounted) {
231
- setIsAvailable(result);
232
- }
233
- } catch (error) {
234
- if (isMounted) {
235
- setIsAvailable(false);
236
- }
237
- } finally {
238
- if (isMounted) {
239
- setIsLoading(false);
240
- }
241
- }
242
- };
243
- checkPhantomLogin();
244
- return () => {
245
- isMounted = false;
246
- };
247
- }, []);
248
- return { isLoading, isAvailable };
249
- }
250
-
251
585
  // src/hooks/useAutoConfirm.ts
252
- import { useCallback as useCallback3, useState as useState5, useEffect as useEffect4 } from "react";
586
+ import { useCallback as useCallback5, useState as useState8, useEffect as useEffect5 } from "react";
253
587
  function useAutoConfirm() {
254
- const { sdk, currentProviderType } = usePhantom();
255
- const [status, setStatus] = useState5(null);
256
- const [supportedChains, setSupportedChains] = useState5(null);
257
- const [isLoading, setIsLoading] = useState5(false);
258
- const [error, setError] = useState5(null);
259
- const isInjected = currentProviderType === "injected";
260
- const enable = useCallback3(
588
+ const { sdk, user } = usePhantom();
589
+ const [status, setStatus] = useState8(null);
590
+ const [supportedChains, setSupportedChains] = useState8(null);
591
+ const [isLoading, setIsLoading] = useState8(false);
592
+ const [error, setError] = useState8(null);
593
+ const isInjected = user?.authProvider === "injected";
594
+ const enable = useCallback5(
261
595
  async (params) => {
262
596
  if (!sdk) {
263
597
  throw new Error("SDK not initialized");
@@ -281,7 +615,7 @@ function useAutoConfirm() {
281
615
  },
282
616
  [sdk, isInjected]
283
617
  );
284
- const disable = useCallback3(async () => {
618
+ const disable = useCallback5(async () => {
285
619
  if (!sdk) {
286
620
  throw new Error("SDK not initialized");
287
621
  }
@@ -302,7 +636,7 @@ function useAutoConfirm() {
302
636
  setIsLoading(false);
303
637
  }
304
638
  }, [sdk, isInjected]);
305
- const refetch = useCallback3(async () => {
639
+ const refetch = useCallback5(async () => {
306
640
  if (!sdk || !isInjected) {
307
641
  return;
308
642
  }
@@ -322,7 +656,7 @@ function useAutoConfirm() {
322
656
  setIsLoading(false);
323
657
  }
324
658
  }, [sdk, isInjected]);
325
- useEffect4(() => {
659
+ useEffect5(() => {
326
660
  if (sdk && isInjected) {
327
661
  refetch();
328
662
  } else {
@@ -378,14 +712,78 @@ function useEthereum() {
378
712
  };
379
713
  }
380
714
 
715
+ // src/hooks/index.ts
716
+ import { useTheme as useTheme3 } from "@phantom/wallet-sdk-ui";
717
+
718
+ // src/components/ConnectButton.tsx
719
+ import { useMemo as useMemo4 } from "react";
720
+ import { useTheme as useTheme4 } from "@phantom/wallet-sdk-ui";
721
+ import { jsx as jsx5 } from "react/jsx-runtime";
722
+ function ConnectButton({ addressType, fullWidth = false }) {
723
+ const theme = useTheme4();
724
+ const { open } = useModal();
725
+ const { isConnected, addresses } = usePhantom();
726
+ const displayAddress = useMemo4(() => {
727
+ if (!addresses || addresses.length === 0)
728
+ return null;
729
+ if (addressType) {
730
+ return addresses.find((addr) => addr.addressType === addressType);
731
+ }
732
+ return addresses[0];
733
+ }, [addresses, addressType]);
734
+ const truncatedAddress = useMemo4(() => {
735
+ if (!displayAddress)
736
+ return "";
737
+ const addr = displayAddress.address;
738
+ if (addr.length <= 12)
739
+ return addr;
740
+ return `${addr.slice(0, 6)}...${addr.slice(-4)}`;
741
+ }, [displayAddress]);
742
+ const buttonStyle = {
743
+ width: fullWidth ? "100%" : "auto",
744
+ padding: "12px 16px",
745
+ border: "none",
746
+ borderRadius: theme.borderRadius,
747
+ fontFamily: theme.typography.captionBold.fontFamily,
748
+ fontSize: theme.typography.captionBold.fontSize,
749
+ fontStyle: theme.typography.captionBold.fontStyle,
750
+ fontWeight: theme.typography.captionBold.fontWeight,
751
+ lineHeight: theme.typography.captionBold.lineHeight,
752
+ letterSpacing: theme.typography.captionBold.letterSpacing,
753
+ cursor: "pointer",
754
+ transition: "background-color 0.2s",
755
+ display: "flex",
756
+ alignItems: "center",
757
+ justifyContent: "center",
758
+ gap: "8px",
759
+ background: theme.aux,
760
+ color: theme.text
761
+ };
762
+ const connectedButtonStyle = {
763
+ ...buttonStyle,
764
+ background: theme.aux,
765
+ cursor: "pointer"
766
+ };
767
+ if (isConnected && displayAddress) {
768
+ return /* @__PURE__ */ jsx5("button", { style: connectedButtonStyle, onClick: open, children: /* @__PURE__ */ jsx5("span", { style: { fontFamily: "monospace" }, children: truncatedAddress }) });
769
+ }
770
+ return /* @__PURE__ */ jsx5("button", { style: buttonStyle, onClick: open, children: "Connect Wallet" });
771
+ }
772
+
381
773
  // src/index.ts
382
- import { NetworkId, AddressType, DebugLevel, debug } from "@phantom/browser-sdk";
774
+ import { darkTheme as darkTheme2, lightTheme, mergeTheme as mergeTheme2 } from "@phantom/wallet-sdk-ui";
775
+ import { NetworkId, AddressType, DebugLevel, debug, isMobileDevice as isMobileDevice3 } from "@phantom/browser-sdk";
383
776
  export {
384
777
  AddressType,
778
+ ConnectButton,
385
779
  DebugLevel,
386
780
  NetworkId,
387
781
  PhantomProvider,
782
+ darkTheme2 as darkTheme,
388
783
  debug,
784
+ isMobileDevice3 as isMobileDevice,
785
+ lightTheme,
786
+ mergeTheme2 as mergeTheme,
389
787
  useAccounts,
390
788
  useAutoConfirm,
391
789
  useConnect,
@@ -393,6 +791,8 @@ export {
393
791
  useEthereum,
394
792
  useIsExtensionInstalled,
395
793
  useIsPhantomLoginAvailable,
794
+ useModal,
396
795
  usePhantom,
397
- useSolana
796
+ useSolana,
797
+ useTheme3 as useTheme
398
798
  };