@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/README.md +300 -48
- package/dist/index.d.ts +39 -17
- package/dist/index.js +582 -182
- package/dist/index.mjs +578 -178
- package/package.json +5 -4
package/dist/index.mjs
CHANGED
|
@@ -1,31 +1,488 @@
|
|
|
1
1
|
// src/PhantomProvider.tsx
|
|
2
|
-
import {
|
|
2
|
+
import { useState as useState7, useEffect as useEffect4, useMemo as useMemo3 } from "react";
|
|
3
3
|
import { BrowserSDK } from "@phantom/browser-sdk";
|
|
4
|
-
import {
|
|
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
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
532
|
+
useEffect4(() => {
|
|
77
533
|
if (!debugConfig || !sdk)
|
|
78
534
|
return;
|
|
79
535
|
sdk.configureDebug(debugConfig);
|
|
80
536
|
}, [sdk, debugConfig]);
|
|
81
|
-
|
|
537
|
+
useEffect4(() => {
|
|
82
538
|
if (!isClient || !sdk)
|
|
83
539
|
return;
|
|
84
540
|
const initialize = async () => {
|
|
85
541
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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,
|
|
99
|
-
const value =
|
|
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
|
-
[
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
|
586
|
+
import { useCallback as useCallback5, useState as useState8, useEffect as useEffect5 } from "react";
|
|
253
587
|
function useAutoConfirm() {
|
|
254
|
-
const { sdk,
|
|
255
|
-
const [status, setStatus] =
|
|
256
|
-
const [supportedChains, setSupportedChains] =
|
|
257
|
-
const [isLoading, setIsLoading] =
|
|
258
|
-
const [error, setError] =
|
|
259
|
-
const isInjected =
|
|
260
|
-
const enable =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 {
|
|
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
|
};
|