@phantom/react-sdk 1.0.0-beta.8 → 1.0.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/README.md +609 -58
- package/dist/index.d.ts +75 -22
- package/dist/index.js +969 -179
- package/dist/index.mjs +975 -176
- package/package.json +13 -6
package/dist/index.mjs
CHANGED
|
@@ -1,50 +1,798 @@
|
|
|
1
1
|
// src/PhantomProvider.tsx
|
|
2
|
-
import {
|
|
2
|
+
import { useState as useState8, useEffect as useEffect5, useMemo as useMemo3, useCallback as useCallback6 } 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
|
-
|
|
16
|
-
|
|
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 useState7, useCallback as useCallback5, 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 useState4, useCallback as useCallback3, useMemo } from "react";
|
|
41
|
+
import { isMobileDevice } from "@phantom/browser-sdk";
|
|
42
|
+
import {
|
|
43
|
+
Button,
|
|
44
|
+
LoginWithPhantomButton,
|
|
45
|
+
Icon as Icon2,
|
|
46
|
+
BoundedIcon,
|
|
47
|
+
Text,
|
|
48
|
+
hexToRgba,
|
|
49
|
+
useTheme as useTheme2,
|
|
50
|
+
ModalHeader
|
|
51
|
+
} from "@phantom/wallet-sdk-ui";
|
|
52
|
+
import { getProviderName } from "@phantom/constants";
|
|
53
|
+
|
|
54
|
+
// src/hooks/useIsExtensionInstalled.ts
|
|
55
|
+
import * as React from "react";
|
|
56
|
+
import { waitForPhantomExtension } from "@phantom/browser-sdk";
|
|
57
|
+
function useIsExtensionInstalled() {
|
|
58
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
59
|
+
const [isInstalled, setIsInstalled] = React.useState(false);
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
let isMounted = true;
|
|
62
|
+
const checkExtension = async () => {
|
|
63
|
+
try {
|
|
64
|
+
setIsLoading(true);
|
|
65
|
+
const result = await waitForPhantomExtension(3e3);
|
|
66
|
+
if (isMounted) {
|
|
67
|
+
setIsInstalled(result);
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (isMounted) {
|
|
71
|
+
setIsInstalled(false);
|
|
72
|
+
}
|
|
73
|
+
} finally {
|
|
74
|
+
if (isMounted) {
|
|
75
|
+
setIsLoading(false);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
checkExtension();
|
|
80
|
+
return () => {
|
|
81
|
+
isMounted = false;
|
|
82
|
+
};
|
|
83
|
+
}, []);
|
|
84
|
+
return { isLoading, isInstalled };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/hooks/useIsPhantomLoginAvailable.ts
|
|
88
|
+
import * as React2 from "react";
|
|
89
|
+
import { isPhantomLoginAvailable } from "@phantom/browser-sdk";
|
|
90
|
+
function useIsPhantomLoginAvailable() {
|
|
91
|
+
const [isLoading, setIsLoading] = React2.useState(true);
|
|
92
|
+
const [isAvailable, setIsAvailable] = React2.useState(false);
|
|
93
|
+
React2.useEffect(() => {
|
|
94
|
+
let isMounted = true;
|
|
95
|
+
const checkPhantomLogin = async () => {
|
|
96
|
+
try {
|
|
97
|
+
setIsLoading(true);
|
|
98
|
+
const result = await isPhantomLoginAvailable(3e3);
|
|
99
|
+
if (isMounted) {
|
|
100
|
+
setIsAvailable(result);
|
|
101
|
+
}
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (isMounted) {
|
|
104
|
+
setIsAvailable(false);
|
|
105
|
+
}
|
|
106
|
+
} finally {
|
|
107
|
+
if (isMounted) {
|
|
108
|
+
setIsLoading(false);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
checkPhantomLogin();
|
|
113
|
+
return () => {
|
|
114
|
+
isMounted = false;
|
|
115
|
+
};
|
|
116
|
+
}, []);
|
|
117
|
+
return { isLoading, isAvailable };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/hooks/useConnect.ts
|
|
121
|
+
import { useCallback } from "react";
|
|
122
|
+
function useConnect() {
|
|
123
|
+
const { sdk, isConnecting, isLoading, errors } = usePhantom();
|
|
124
|
+
const connect = useCallback(
|
|
125
|
+
async (options) => {
|
|
126
|
+
if (!sdk) {
|
|
127
|
+
throw new Error("SDK not initialized");
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const result = await sdk.connect(options);
|
|
131
|
+
return result;
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.error("Error connecting to Phantom:", err);
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
[sdk]
|
|
17
138
|
);
|
|
18
|
-
|
|
19
|
-
|
|
139
|
+
return {
|
|
140
|
+
connect,
|
|
141
|
+
isConnecting,
|
|
142
|
+
isLoading,
|
|
143
|
+
error: errors.connect
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/hooks/useDiscoveredWallets.ts
|
|
148
|
+
import { useCallback as useCallback2, useState as useState3, useEffect as useEffect3 } from "react";
|
|
149
|
+
function useDiscoveredWallets() {
|
|
150
|
+
const { sdk } = usePhantom();
|
|
151
|
+
const [wallets, setWallets] = useState3([]);
|
|
152
|
+
const [isLoading, setIsLoading] = useState3(true);
|
|
153
|
+
const [error, setError] = useState3(null);
|
|
154
|
+
const refetch = useCallback2(async () => {
|
|
155
|
+
if (!sdk) {
|
|
156
|
+
setWallets([]);
|
|
157
|
+
setError(null);
|
|
158
|
+
setIsLoading(false);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
setIsLoading(true);
|
|
163
|
+
setError(null);
|
|
164
|
+
const initialWallets = sdk.getDiscoveredWallets();
|
|
165
|
+
if (initialWallets.length > 0) {
|
|
166
|
+
setWallets(initialWallets);
|
|
167
|
+
setIsLoading(false);
|
|
168
|
+
} else {
|
|
169
|
+
await sdk.discoverWallets();
|
|
170
|
+
const discoveredWallets = sdk.getDiscoveredWallets();
|
|
171
|
+
setWallets(discoveredWallets);
|
|
172
|
+
setIsLoading(false);
|
|
173
|
+
}
|
|
174
|
+
} catch (err) {
|
|
175
|
+
const error2 = err instanceof Error ? err : new Error("Failed to fetch discovered wallets");
|
|
176
|
+
setError(error2);
|
|
177
|
+
setWallets([]);
|
|
178
|
+
setIsLoading(false);
|
|
179
|
+
}
|
|
180
|
+
}, [sdk]);
|
|
181
|
+
useEffect3(() => {
|
|
182
|
+
refetch();
|
|
183
|
+
}, [refetch]);
|
|
184
|
+
return {
|
|
185
|
+
wallets,
|
|
186
|
+
isLoading,
|
|
187
|
+
error,
|
|
188
|
+
refetch
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/components/ChainIcon.tsx
|
|
193
|
+
import { Icon, useTheme } from "@phantom/wallet-sdk-ui";
|
|
194
|
+
import { jsx } from "react/jsx-runtime";
|
|
195
|
+
var IconWrapper = ({ children }) => {
|
|
196
|
+
const theme = useTheme();
|
|
197
|
+
return /* @__PURE__ */ jsx(
|
|
198
|
+
"span",
|
|
199
|
+
{
|
|
200
|
+
style: {
|
|
201
|
+
display: "inline-flex",
|
|
202
|
+
alignItems: "center",
|
|
203
|
+
justifyContent: "center",
|
|
204
|
+
borderRadius: "4px",
|
|
205
|
+
backgroundColor: theme.aux,
|
|
206
|
+
color: theme.text,
|
|
207
|
+
padding: "2px"
|
|
208
|
+
},
|
|
209
|
+
children
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
};
|
|
213
|
+
function ChainIcon({ addressType, size = 8 }) {
|
|
214
|
+
const theme = useTheme();
|
|
215
|
+
const type = addressType.toLowerCase();
|
|
216
|
+
if (type.includes("solana")) {
|
|
217
|
+
return /* @__PURE__ */ jsx(IconWrapper, { children: /* @__PURE__ */ jsx(Icon, { type: "solana", size, color: theme.text }) });
|
|
218
|
+
}
|
|
219
|
+
if (type.includes("ethereum") || type.includes("evm")) {
|
|
220
|
+
return /* @__PURE__ */ jsx(IconWrapper, { children: /* @__PURE__ */ jsx(Icon, { type: "ethereum", size, color: theme.text }) });
|
|
221
|
+
}
|
|
222
|
+
if (type.includes("bitcoin")) {
|
|
223
|
+
return /* @__PURE__ */ jsx(IconWrapper, { children: /* @__PURE__ */ jsx(Icon, { type: "bitcoin", size, color: theme.text }) });
|
|
224
|
+
}
|
|
225
|
+
if (type.includes("sui")) {
|
|
226
|
+
return /* @__PURE__ */ jsx(IconWrapper, { children: /* @__PURE__ */ jsx(Icon, { type: "sui", size, color: theme.text }) });
|
|
227
|
+
}
|
|
228
|
+
return /* @__PURE__ */ jsx(
|
|
229
|
+
"span",
|
|
230
|
+
{
|
|
231
|
+
style: {
|
|
232
|
+
display: "inline-flex",
|
|
233
|
+
alignItems: "center",
|
|
234
|
+
justifyContent: "center",
|
|
235
|
+
borderRadius: "4px",
|
|
236
|
+
backgroundColor: theme.aux,
|
|
237
|
+
color: theme.text,
|
|
238
|
+
fontSize: "6px",
|
|
239
|
+
fontWeight: "bold",
|
|
240
|
+
lineHeight: "1",
|
|
241
|
+
padding: "2px"
|
|
242
|
+
},
|
|
243
|
+
title: addressType,
|
|
244
|
+
children: addressType.charAt(0).toUpperCase()
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// src/components/ConnectModalContent.tsx
|
|
250
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
251
|
+
function ConnectModalContent({
|
|
252
|
+
appIcon,
|
|
253
|
+
appName = "App Name",
|
|
254
|
+
onClose,
|
|
255
|
+
hideCloseButton = false
|
|
256
|
+
}) {
|
|
257
|
+
const theme = useTheme2();
|
|
258
|
+
const { isLoading, allowedProviders } = usePhantom();
|
|
259
|
+
const baseConnect = useConnect();
|
|
260
|
+
const isExtensionInstalled = useIsExtensionInstalled();
|
|
261
|
+
const isPhantomLoginAvailable2 = useIsPhantomLoginAvailable();
|
|
262
|
+
const isMobile = useMemo(() => isMobileDevice(), []);
|
|
263
|
+
const { wallets: discoveredWallets } = useDiscoveredWallets();
|
|
264
|
+
const [isConnecting, setIsConnecting] = useState4(false);
|
|
265
|
+
const [error, setError] = useState4(null);
|
|
266
|
+
const [providerType, setProviderType] = useState4(null);
|
|
267
|
+
const [showOtherWallets, setShowOtherWallets] = useState4(false);
|
|
268
|
+
const [selectedWalletId, setSelectedWalletId] = useState4(null);
|
|
269
|
+
const isConnectingState = baseConnect.isConnecting || isConnecting;
|
|
270
|
+
const errorState = baseConnect.error ? baseConnect.error.message : error;
|
|
271
|
+
const showDivider = !(allowedProviders.length === 1 && allowedProviders.includes("injected"));
|
|
272
|
+
const shouldShowOtherWalletsButton = discoveredWallets.length > 2;
|
|
273
|
+
const walletsToShowInline = shouldShowOtherWalletsButton ? [] : discoveredWallets;
|
|
274
|
+
const connectWithAuthProvider = useCallback3(
|
|
275
|
+
async (provider, walletId) => {
|
|
276
|
+
try {
|
|
277
|
+
setIsConnecting(true);
|
|
278
|
+
setError(null);
|
|
279
|
+
setProviderType(provider);
|
|
280
|
+
setSelectedWalletId(walletId || null);
|
|
281
|
+
await baseConnect.connect({ provider, walletId });
|
|
282
|
+
onClose();
|
|
283
|
+
} catch {
|
|
284
|
+
const wallet = discoveredWallets.find((w) => w.id === walletId);
|
|
285
|
+
const providerName = wallet?.name || getProviderName(provider);
|
|
286
|
+
setError(`Failed to connect to ${providerName}`);
|
|
287
|
+
} finally {
|
|
288
|
+
setIsConnecting(false);
|
|
289
|
+
setProviderType(null);
|
|
290
|
+
setSelectedWalletId(null);
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
[baseConnect, discoveredWallets, onClose]
|
|
294
|
+
);
|
|
295
|
+
const connectWithWallet = useCallback3(
|
|
296
|
+
async (wallet) => {
|
|
297
|
+
await connectWithAuthProvider("injected", wallet.id);
|
|
298
|
+
},
|
|
299
|
+
[connectWithAuthProvider]
|
|
300
|
+
);
|
|
301
|
+
const connectWithDeeplink = useCallback3(async () => {
|
|
302
|
+
try {
|
|
303
|
+
setIsConnecting(true);
|
|
304
|
+
setError(null);
|
|
305
|
+
setProviderType("deeplink");
|
|
306
|
+
await baseConnect.connect({ provider: "deeplink" });
|
|
307
|
+
onClose();
|
|
308
|
+
} catch (error2) {
|
|
309
|
+
const errorMessage = error2 instanceof Error ? error2.message : "Failed to open deeplink";
|
|
310
|
+
setError(errorMessage);
|
|
311
|
+
} finally {
|
|
312
|
+
setIsConnecting(false);
|
|
313
|
+
setProviderType(null);
|
|
314
|
+
}
|
|
315
|
+
}, [baseConnect, onClose]);
|
|
316
|
+
const appIconStyle = {
|
|
317
|
+
width: "56px",
|
|
318
|
+
height: "56px",
|
|
319
|
+
borderRadius: "50%",
|
|
320
|
+
display: "block",
|
|
321
|
+
objectFit: "cover",
|
|
322
|
+
marginBottom: "12px"
|
|
323
|
+
};
|
|
324
|
+
const connectContentContainerStyle = {
|
|
325
|
+
transition: "opacity 0.15s ease-in-out, transform 0.15s ease-in-out",
|
|
326
|
+
display: "flex",
|
|
327
|
+
flexDirection: "column",
|
|
328
|
+
alignItems: "center",
|
|
329
|
+
gap: "12px",
|
|
330
|
+
padding: "0 32px"
|
|
331
|
+
};
|
|
332
|
+
const otherWalletsContainerStyle = {
|
|
333
|
+
display: "flex",
|
|
334
|
+
flexDirection: "column",
|
|
335
|
+
alignItems: "center",
|
|
336
|
+
gap: "12px",
|
|
337
|
+
maxHeight: "480px",
|
|
338
|
+
overflowY: "auto",
|
|
339
|
+
padding: "0 32px 32px 32px",
|
|
340
|
+
transition: "opacity 0.15s ease-in-out, transform 0.15s ease-in-out"
|
|
341
|
+
};
|
|
342
|
+
const dividerStyle = {
|
|
343
|
+
display: "flex",
|
|
344
|
+
alignItems: "center",
|
|
345
|
+
width: "100%",
|
|
346
|
+
margin: "12px 0",
|
|
347
|
+
...theme.typography.caption,
|
|
348
|
+
color: theme.secondary,
|
|
349
|
+
textTransform: "uppercase"
|
|
350
|
+
};
|
|
351
|
+
const dividerLineStyle = {
|
|
352
|
+
flex: 1,
|
|
353
|
+
height: "1px",
|
|
354
|
+
backgroundColor: hexToRgba(theme.secondary, 0.1)
|
|
355
|
+
};
|
|
356
|
+
const dividerTextStyle = {
|
|
357
|
+
padding: "0 12px"
|
|
358
|
+
};
|
|
359
|
+
const errorStyle = {
|
|
360
|
+
backgroundColor: "rgba(220, 53, 69, 0.1)",
|
|
361
|
+
color: "#ff6b6b",
|
|
362
|
+
border: "1px solid rgba(220, 53, 69, 0.3)",
|
|
363
|
+
borderRadius: theme.borderRadius,
|
|
364
|
+
boxSizing: "border-box",
|
|
365
|
+
padding: "12px",
|
|
366
|
+
width: "100%",
|
|
367
|
+
fontSize: "14px"
|
|
368
|
+
};
|
|
369
|
+
const loadingContainerStyle = {
|
|
370
|
+
display: "flex",
|
|
371
|
+
flexDirection: "column",
|
|
372
|
+
alignItems: "center",
|
|
373
|
+
justifyContent: "center",
|
|
374
|
+
padding: "24px",
|
|
375
|
+
gap: "12px"
|
|
376
|
+
};
|
|
377
|
+
const spinnerStyle = {
|
|
378
|
+
width: "40px",
|
|
379
|
+
height: "40px",
|
|
380
|
+
border: `3px solid ${theme.secondary}`,
|
|
381
|
+
borderTop: `3px solid ${theme.brand}`,
|
|
382
|
+
borderRadius: "50%",
|
|
383
|
+
animation: "spin 1s linear infinite"
|
|
384
|
+
};
|
|
385
|
+
const walletIconStyle = {
|
|
386
|
+
width: "32px",
|
|
387
|
+
height: "32px",
|
|
388
|
+
borderRadius: "8px",
|
|
389
|
+
objectFit: "cover"
|
|
390
|
+
};
|
|
391
|
+
const walletButtonContentStyle = {
|
|
392
|
+
display: "flex",
|
|
393
|
+
alignItems: "center",
|
|
394
|
+
justifyContent: "space-between",
|
|
395
|
+
gap: "8px",
|
|
396
|
+
width: "100%"
|
|
397
|
+
};
|
|
398
|
+
const walletButtonLeftStyle = {
|
|
399
|
+
display: "flex",
|
|
400
|
+
alignItems: "center",
|
|
401
|
+
gap: "8px",
|
|
402
|
+
flex: 1
|
|
403
|
+
};
|
|
404
|
+
const walletNameContainerStyle = {
|
|
405
|
+
display: "flex",
|
|
406
|
+
flexDirection: "column",
|
|
407
|
+
gap: "4px",
|
|
408
|
+
alignItems: "flex-start"
|
|
409
|
+
};
|
|
410
|
+
const chainIndicatorsStyle = {
|
|
411
|
+
display: "flex",
|
|
412
|
+
alignItems: "center",
|
|
413
|
+
gap: "4px"
|
|
414
|
+
};
|
|
415
|
+
const walletButtonRightStyle = {
|
|
416
|
+
display: "flex",
|
|
417
|
+
alignItems: "center",
|
|
418
|
+
gap: "8px",
|
|
419
|
+
color: theme.secondary
|
|
420
|
+
};
|
|
421
|
+
const footerStyle = {
|
|
422
|
+
display: "flex",
|
|
423
|
+
padding: "16px",
|
|
424
|
+
justifyContent: "center",
|
|
425
|
+
alignItems: "center",
|
|
426
|
+
gap: "4px",
|
|
427
|
+
borderTop: "1px solid rgba(152, 151, 156, 0.10)",
|
|
428
|
+
...theme.typography.caption,
|
|
429
|
+
color: theme.secondary
|
|
430
|
+
};
|
|
431
|
+
const contentWrapperStyle = {
|
|
432
|
+
display: "flex",
|
|
433
|
+
flexDirection: "column",
|
|
434
|
+
justifyContent: "space-between",
|
|
435
|
+
gap: "24px"
|
|
436
|
+
};
|
|
437
|
+
return /* @__PURE__ */ jsxs("div", { style: contentWrapperStyle, children: [
|
|
438
|
+
/* @__PURE__ */ jsx2("style", { children: `
|
|
439
|
+
@keyframes spin {
|
|
440
|
+
0% { transform: rotate(0deg); }
|
|
441
|
+
100% { transform: rotate(360deg); }
|
|
442
|
+
}
|
|
443
|
+
` }),
|
|
444
|
+
isLoading || baseConnect.isConnecting ? /* @__PURE__ */ jsxs("div", { style: loadingContainerStyle, children: [
|
|
445
|
+
/* @__PURE__ */ jsx2("div", { style: spinnerStyle }),
|
|
446
|
+
/* @__PURE__ */ jsx2(Text, { variant: "label", color: theme.secondary, children: "Loading..." })
|
|
447
|
+
] }) : showOtherWallets ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
448
|
+
/* @__PURE__ */ jsx2(
|
|
449
|
+
ModalHeader,
|
|
450
|
+
{
|
|
451
|
+
goBack: true,
|
|
452
|
+
onGoBack: () => {
|
|
453
|
+
setError(null);
|
|
454
|
+
setShowOtherWallets(false);
|
|
455
|
+
},
|
|
456
|
+
title: "Other Wallets",
|
|
457
|
+
onClose,
|
|
458
|
+
hideCloseButton
|
|
459
|
+
}
|
|
460
|
+
),
|
|
461
|
+
/* @__PURE__ */ jsxs("div", { style: otherWalletsContainerStyle, children: [
|
|
462
|
+
errorState && /* @__PURE__ */ jsx2("div", { style: errorStyle, children: errorState }),
|
|
463
|
+
discoveredWallets.map((wallet) => /* @__PURE__ */ jsx2(
|
|
464
|
+
Button,
|
|
465
|
+
{
|
|
466
|
+
onClick: () => connectWithWallet(wallet),
|
|
467
|
+
disabled: isConnectingState,
|
|
468
|
+
isLoading: isConnectingState && providerType === "injected" && selectedWalletId === wallet.id,
|
|
469
|
+
fullWidth: true,
|
|
470
|
+
children: /* @__PURE__ */ jsxs("span", { style: walletButtonContentStyle, children: [
|
|
471
|
+
/* @__PURE__ */ jsxs("span", { style: walletButtonLeftStyle, children: [
|
|
472
|
+
wallet.id === "phantom" ? /* @__PURE__ */ jsx2(BoundedIcon, { type: "phantom", size: 20, background: "#aba0f2", color: "white" }) : wallet.icon ? /* @__PURE__ */ jsx2("img", { src: wallet.icon, alt: wallet.name, style: walletIconStyle }) : /* @__PURE__ */ jsx2(BoundedIcon, { type: "wallet", size: 20, background: theme.aux, color: theme.text }),
|
|
473
|
+
/* @__PURE__ */ jsx2("span", { style: walletNameContainerStyle, children: /* @__PURE__ */ jsx2(Text, { variant: "captionBold", children: wallet.name }) })
|
|
474
|
+
] }),
|
|
475
|
+
/* @__PURE__ */ jsxs("span", { style: walletButtonRightStyle, children: [
|
|
476
|
+
wallet.addressTypes && wallet.addressTypes.length > 0 && /* @__PURE__ */ jsx2("span", { style: chainIndicatorsStyle, children: wallet.addressTypes.map((addressType) => /* @__PURE__ */ jsx2("span", { children: /* @__PURE__ */ jsx2(ChainIcon, { addressType, size: 8 }) }, `${wallet.id}-chain-${addressType}`)) }),
|
|
477
|
+
/* @__PURE__ */ jsx2(Icon2, { type: "chevron-right", size: 16, color: theme.secondary })
|
|
478
|
+
] })
|
|
479
|
+
] })
|
|
480
|
+
},
|
|
481
|
+
wallet.id
|
|
482
|
+
))
|
|
483
|
+
] })
|
|
484
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
485
|
+
/* @__PURE__ */ jsx2(ModalHeader, { title: "Login or Sign Up", onClose, hideCloseButton }),
|
|
486
|
+
/* @__PURE__ */ jsxs("div", { style: connectContentContainerStyle, children: [
|
|
487
|
+
appIcon && /* @__PURE__ */ jsx2("img", { src: appIcon, alt: appName, style: appIconStyle }),
|
|
488
|
+
errorState && /* @__PURE__ */ jsx2("div", { style: errorStyle, children: errorState }),
|
|
489
|
+
isMobile && !isExtensionInstalled.isInstalled && allowedProviders.includes("deeplink") && /* @__PURE__ */ jsx2(
|
|
490
|
+
LoginWithPhantomButton,
|
|
491
|
+
{
|
|
492
|
+
testId: "deeplink-button",
|
|
493
|
+
onClick: connectWithDeeplink,
|
|
494
|
+
disabled: isConnectingState,
|
|
495
|
+
isLoading: isConnectingState && providerType === "deeplink",
|
|
496
|
+
fullWidth: true,
|
|
497
|
+
children: isConnecting && providerType === "deeplink" ? "Opening Phantom..." : "Open in Phantom App"
|
|
498
|
+
}
|
|
499
|
+
),
|
|
500
|
+
!isMobile && allowedProviders.includes("phantom") && isPhantomLoginAvailable2.isAvailable && /* @__PURE__ */ jsx2(
|
|
501
|
+
LoginWithPhantomButton,
|
|
502
|
+
{
|
|
503
|
+
testId: "login-with-phantom-button",
|
|
504
|
+
onClick: () => connectWithAuthProvider("phantom"),
|
|
505
|
+
disabled: isConnectingState,
|
|
506
|
+
isLoading: isConnectingState && providerType === "phantom"
|
|
507
|
+
}
|
|
508
|
+
),
|
|
509
|
+
allowedProviders.includes("google") && !(isMobile && isExtensionInstalled.isInstalled) && /* @__PURE__ */ jsx2(
|
|
510
|
+
Button,
|
|
511
|
+
{
|
|
512
|
+
onClick: () => connectWithAuthProvider("google"),
|
|
513
|
+
disabled: isConnectingState,
|
|
514
|
+
isLoading: isConnectingState && providerType === "google",
|
|
515
|
+
fullWidth: true,
|
|
516
|
+
children: /* @__PURE__ */ jsxs("span", { style: walletButtonContentStyle, children: [
|
|
517
|
+
/* @__PURE__ */ jsxs("span", { style: walletButtonLeftStyle, children: [
|
|
518
|
+
/* @__PURE__ */ jsx2(Icon2, { type: "google", size: 20 }),
|
|
519
|
+
/* @__PURE__ */ jsx2(Text, { variant: "captionBold", children: "Continue with Google" })
|
|
520
|
+
] }),
|
|
521
|
+
/* @__PURE__ */ jsx2("span", { style: walletButtonRightStyle, children: /* @__PURE__ */ jsx2(Icon2, { type: "chevron-right", size: 16, color: theme.secondary }) })
|
|
522
|
+
] })
|
|
523
|
+
}
|
|
524
|
+
),
|
|
525
|
+
allowedProviders.includes("apple") && /* @__PURE__ */ jsx2(
|
|
526
|
+
Button,
|
|
527
|
+
{
|
|
528
|
+
onClick: () => connectWithAuthProvider("apple"),
|
|
529
|
+
disabled: isConnectingState,
|
|
530
|
+
isLoading: isConnectingState && providerType === "apple",
|
|
531
|
+
fullWidth: true,
|
|
532
|
+
children: /* @__PURE__ */ jsxs("span", { style: walletButtonContentStyle, children: [
|
|
533
|
+
/* @__PURE__ */ jsxs("span", { style: walletButtonLeftStyle, children: [
|
|
534
|
+
/* @__PURE__ */ jsx2(Icon2, { type: "apple", size: 20 }),
|
|
535
|
+
/* @__PURE__ */ jsx2(Text, { variant: "captionBold", children: "Continue with Apple" })
|
|
536
|
+
] }),
|
|
537
|
+
/* @__PURE__ */ jsx2("span", { style: walletButtonRightStyle, children: /* @__PURE__ */ jsx2(Icon2, { type: "chevron-right", size: 16, color: theme.secondary }) })
|
|
538
|
+
] })
|
|
539
|
+
}
|
|
540
|
+
),
|
|
541
|
+
allowedProviders.includes("injected") && (isExtensionInstalled.isInstalled || discoveredWallets.length > 0) && (!isMobile || isExtensionInstalled.isInstalled) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
542
|
+
showDivider && /* @__PURE__ */ jsxs("div", { style: dividerStyle, children: [
|
|
543
|
+
/* @__PURE__ */ jsx2("div", { style: dividerLineStyle }),
|
|
544
|
+
/* @__PURE__ */ jsx2("span", { style: dividerTextStyle, children: "OR" }),
|
|
545
|
+
/* @__PURE__ */ jsx2("div", { style: dividerLineStyle })
|
|
546
|
+
] }),
|
|
547
|
+
walletsToShowInline.map((wallet) => /* @__PURE__ */ jsx2(
|
|
548
|
+
Button,
|
|
549
|
+
{
|
|
550
|
+
onClick: () => connectWithWallet(wallet),
|
|
551
|
+
disabled: isConnectingState,
|
|
552
|
+
isLoading: isConnectingState && providerType === "injected" && selectedWalletId === wallet.id,
|
|
553
|
+
fullWidth: true,
|
|
554
|
+
children: /* @__PURE__ */ jsxs("span", { style: walletButtonContentStyle, children: [
|
|
555
|
+
/* @__PURE__ */ jsxs("span", { style: walletButtonLeftStyle, children: [
|
|
556
|
+
wallet.id === "phantom" ? /* @__PURE__ */ jsx2(BoundedIcon, { type: "phantom", size: 20, background: "#aba0f2", color: "white" }) : wallet.icon ? /* @__PURE__ */ jsx2("img", { src: wallet.icon, alt: wallet.name, style: walletIconStyle }) : /* @__PURE__ */ jsx2(BoundedIcon, { type: "wallet", size: 10, background: theme.aux, color: theme.text }),
|
|
557
|
+
/* @__PURE__ */ jsx2("span", { style: walletNameContainerStyle, children: /* @__PURE__ */ jsx2(Text, { variant: "captionBold", children: wallet.name }) })
|
|
558
|
+
] }),
|
|
559
|
+
/* @__PURE__ */ jsxs("span", { style: walletButtonRightStyle, children: [
|
|
560
|
+
wallet.addressTypes && wallet.addressTypes.length > 0 && /* @__PURE__ */ jsx2("span", { style: chainIndicatorsStyle, children: wallet.addressTypes.map((addressType) => /* @__PURE__ */ jsx2("span", { children: /* @__PURE__ */ jsx2(ChainIcon, { addressType, size: 8 }) }, `${wallet.id}-chain-${addressType}`)) }),
|
|
561
|
+
/* @__PURE__ */ jsx2(Icon2, { type: "chevron-right", size: 16, color: theme.secondary })
|
|
562
|
+
] })
|
|
563
|
+
] })
|
|
564
|
+
},
|
|
565
|
+
wallet.id
|
|
566
|
+
)),
|
|
567
|
+
shouldShowOtherWalletsButton && /* @__PURE__ */ jsx2(Button, { onClick: () => setShowOtherWallets(true), disabled: isConnectingState, fullWidth: true, children: /* @__PURE__ */ jsxs("span", { style: walletButtonContentStyle, children: [
|
|
568
|
+
/* @__PURE__ */ jsxs("span", { style: walletButtonLeftStyle, children: [
|
|
569
|
+
/* @__PURE__ */ jsx2(BoundedIcon, { type: "wallet", size: 20, background: theme.aux, color: theme.text }),
|
|
570
|
+
/* @__PURE__ */ jsx2(Text, { variant: "captionBold", children: "Other Wallets" })
|
|
571
|
+
] }),
|
|
572
|
+
/* @__PURE__ */ jsx2("span", { style: walletButtonRightStyle, children: /* @__PURE__ */ jsx2(Icon2, { type: "chevron-right", size: 16, color: theme.secondary }) })
|
|
573
|
+
] }) })
|
|
574
|
+
] })
|
|
575
|
+
] }),
|
|
576
|
+
/* @__PURE__ */ jsxs("div", { style: footerStyle, children: [
|
|
577
|
+
/* @__PURE__ */ jsx2(Text, { variant: "label", color: theme.secondary, children: "Powered by" }),
|
|
578
|
+
/* @__PURE__ */ jsx2(Icon2, { type: "phantom", size: 16 }),
|
|
579
|
+
/* @__PURE__ */ jsx2(Text, { variant: "label", color: theme.secondary, children: "Phantom" })
|
|
580
|
+
] })
|
|
581
|
+
] })
|
|
582
|
+
] });
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/components/ConnectedModalContent.tsx
|
|
586
|
+
import { useState as useState6, useEffect as useEffect4 } from "react";
|
|
587
|
+
import { Button as Button2, Text as Text2, useTheme as useTheme3, ModalHeader as ModalHeader2 } from "@phantom/wallet-sdk-ui";
|
|
588
|
+
|
|
589
|
+
// src/hooks/useDisconnect.ts
|
|
590
|
+
import { useCallback as useCallback4, useState as useState5 } from "react";
|
|
591
|
+
function useDisconnect() {
|
|
592
|
+
const { sdk } = usePhantom();
|
|
593
|
+
const [isDisconnecting, setIsDisconnecting] = useState5(false);
|
|
594
|
+
const [error, setError] = useState5(null);
|
|
595
|
+
const disconnect = useCallback4(async () => {
|
|
596
|
+
if (!sdk) {
|
|
597
|
+
throw new Error("SDK not initialized");
|
|
598
|
+
}
|
|
599
|
+
setIsDisconnecting(true);
|
|
600
|
+
setError(null);
|
|
601
|
+
try {
|
|
602
|
+
await sdk.disconnect();
|
|
603
|
+
} catch (err) {
|
|
604
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
605
|
+
setError(error2);
|
|
606
|
+
throw err;
|
|
607
|
+
} finally {
|
|
608
|
+
setIsDisconnecting(false);
|
|
609
|
+
}
|
|
610
|
+
}, [sdk]);
|
|
611
|
+
return {
|
|
612
|
+
disconnect,
|
|
613
|
+
isDisconnecting,
|
|
614
|
+
error
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// src/components/ConnectedModalContent.tsx
|
|
619
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
620
|
+
function ConnectedModalContent({ onClose, hideCloseButton = false }) {
|
|
621
|
+
const theme = useTheme3();
|
|
622
|
+
const { addresses } = usePhantom();
|
|
623
|
+
const { disconnect } = useDisconnect();
|
|
624
|
+
const [isDisconnecting, setIsDisconnecting] = useState6(false);
|
|
625
|
+
const [disconnectError, setDisconnectError] = useState6(null);
|
|
626
|
+
useEffect4(() => {
|
|
627
|
+
setDisconnectError(null);
|
|
628
|
+
}, []);
|
|
629
|
+
const handleDisconnect = async () => {
|
|
630
|
+
try {
|
|
631
|
+
setIsDisconnecting(true);
|
|
632
|
+
setDisconnectError(null);
|
|
633
|
+
await disconnect();
|
|
634
|
+
onClose();
|
|
635
|
+
} catch (err) {
|
|
636
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
637
|
+
setDisconnectError(error);
|
|
638
|
+
} finally {
|
|
639
|
+
setIsDisconnecting(false);
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
const accountListStyle = {
|
|
643
|
+
display: "flex",
|
|
644
|
+
flexDirection: "column",
|
|
645
|
+
gap: "16px",
|
|
646
|
+
width: "100%",
|
|
647
|
+
minWidth: 0,
|
|
648
|
+
boxSizing: "border-box"
|
|
649
|
+
};
|
|
650
|
+
const accountItemStyle = {
|
|
651
|
+
display: "flex",
|
|
652
|
+
flexDirection: "column",
|
|
653
|
+
gap: "8px",
|
|
654
|
+
width: "100%",
|
|
655
|
+
minWidth: 0,
|
|
656
|
+
boxSizing: "border-box"
|
|
657
|
+
};
|
|
658
|
+
const addressTextStyle = {
|
|
659
|
+
fontFamily: "monospace",
|
|
660
|
+
wordBreak: "break-all",
|
|
661
|
+
overflowWrap: "break-word",
|
|
662
|
+
minWidth: 0
|
|
663
|
+
};
|
|
664
|
+
const errorContainerStyle = {
|
|
665
|
+
padding: "12px",
|
|
666
|
+
backgroundColor: "rgba(220, 53, 69, 0.1)",
|
|
667
|
+
borderRadius: theme.borderRadius,
|
|
668
|
+
border: "1px solid rgba(220, 53, 69, 0.3)",
|
|
669
|
+
width: "100%",
|
|
670
|
+
boxSizing: "border-box",
|
|
671
|
+
minWidth: 0
|
|
672
|
+
};
|
|
673
|
+
const contentWrapperStyle = {
|
|
674
|
+
display: "flex",
|
|
675
|
+
flexDirection: "column",
|
|
676
|
+
gap: "24px"
|
|
677
|
+
};
|
|
678
|
+
const accountListContainerStyle = {
|
|
679
|
+
display: "flex",
|
|
680
|
+
flexDirection: "column",
|
|
681
|
+
alignItems: "center",
|
|
682
|
+
gap: "12px",
|
|
683
|
+
padding: "0 32px 24px 32px",
|
|
684
|
+
boxSizing: "border-box",
|
|
685
|
+
width: "100%",
|
|
686
|
+
minWidth: 0
|
|
687
|
+
};
|
|
688
|
+
const disconnectButtonContainerStyle = {
|
|
689
|
+
display: "flex",
|
|
690
|
+
flexDirection: "column",
|
|
691
|
+
alignItems: "center",
|
|
692
|
+
gap: "12px",
|
|
693
|
+
padding: "0 32px 24px 32px",
|
|
694
|
+
boxSizing: "border-box",
|
|
695
|
+
width: "100%",
|
|
696
|
+
minWidth: 0
|
|
697
|
+
};
|
|
698
|
+
return /* @__PURE__ */ jsxs2("div", { style: contentWrapperStyle, children: [
|
|
699
|
+
/* @__PURE__ */ jsx3(ModalHeader2, { title: "Wallet", onClose, hideCloseButton }),
|
|
700
|
+
/* @__PURE__ */ jsxs2("div", { style: accountListContainerStyle, children: [
|
|
701
|
+
disconnectError && /* @__PURE__ */ jsx3("div", { style: errorContainerStyle, children: /* @__PURE__ */ jsx3(Text2, { variant: "caption", color: theme.error, children: "Failed to disconnect" }) }),
|
|
702
|
+
addresses && addresses.length > 0 && /* @__PURE__ */ jsx3("div", { style: accountListStyle, children: addresses.map((account, index) => /* @__PURE__ */ jsxs2("div", { style: accountItemStyle, children: [
|
|
703
|
+
/* @__PURE__ */ jsx3(Text2, { variant: "label", color: theme.secondary, style: { textTransform: "uppercase" }, children: account.addressType }),
|
|
704
|
+
/* @__PURE__ */ jsx3("div", { style: addressTextStyle, children: /* @__PURE__ */ jsx3(Text2, { variant: "caption", children: account.address }) })
|
|
705
|
+
] }, index)) })
|
|
706
|
+
] }),
|
|
707
|
+
/* @__PURE__ */ jsx3("div", { style: disconnectButtonContainerStyle, children: /* @__PURE__ */ jsx3(Button2, { onClick: handleDisconnect, disabled: isDisconnecting, isLoading: isDisconnecting, fullWidth: true, children: /* @__PURE__ */ jsx3(Text2, { variant: "captionBold", children: isDisconnecting ? "Disconnecting..." : "Disconnect" }) }) })
|
|
708
|
+
] });
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// src/components/SpendingLimitModalContent.tsx
|
|
712
|
+
import { Text as Text3, Button as Button3, useTheme as useTheme4 } from "@phantom/wallet-sdk-ui";
|
|
713
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
714
|
+
function SpendingLimitModalContent({ onClose }) {
|
|
715
|
+
const theme = useTheme4();
|
|
716
|
+
return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: 16, padding: 32 }, children: [
|
|
717
|
+
/* @__PURE__ */ jsx4(Text3, { variant: "caption", color: theme.secondary, children: "You've reached the maximum daily limit allowed to spend by this application." }),
|
|
718
|
+
/* @__PURE__ */ jsx4(Button3, { fullWidth: true, onClick: onClose, children: /* @__PURE__ */ jsx4(Text3, { variant: "captionBold", children: "Close" }) })
|
|
719
|
+
] });
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// src/ModalProvider.tsx
|
|
723
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
724
|
+
function ModalProvider({ children, appIcon, appName }) {
|
|
725
|
+
const { isConnected, errors, clearError } = usePhantom();
|
|
726
|
+
const [isModalOpen, setIsModalOpen] = useState7(false);
|
|
727
|
+
const isMobile = useMemo2(() => isMobileDevice2(), []);
|
|
728
|
+
const openModal = useCallback5(() => {
|
|
729
|
+
setIsModalOpen(true);
|
|
730
|
+
}, []);
|
|
731
|
+
const closeModal = useCallback5(() => {
|
|
732
|
+
setIsModalOpen(false);
|
|
733
|
+
clearError("spendingLimit");
|
|
734
|
+
}, [clearError]);
|
|
735
|
+
const isSpendingLimitOpen = !!errors.spendingLimit;
|
|
736
|
+
const modalContextValue = useMemo2(
|
|
737
|
+
() => ({
|
|
738
|
+
isModalOpen,
|
|
739
|
+
openModal,
|
|
740
|
+
closeModal
|
|
741
|
+
}),
|
|
742
|
+
[isModalOpen, openModal, closeModal]
|
|
743
|
+
);
|
|
744
|
+
return /* @__PURE__ */ jsxs4(ModalContext.Provider, { value: modalContextValue, children: [
|
|
745
|
+
children,
|
|
746
|
+
/* @__PURE__ */ jsx5(
|
|
747
|
+
Modal,
|
|
748
|
+
{
|
|
749
|
+
isVisible: isModalOpen || isSpendingLimitOpen,
|
|
750
|
+
onClose: closeModal,
|
|
751
|
+
appIcon,
|
|
752
|
+
appName,
|
|
753
|
+
isMobile,
|
|
754
|
+
children: isSpendingLimitOpen ? /* @__PURE__ */ jsx5(SpendingLimitModalContent, { onClose: closeModal }) : isConnected ? /* @__PURE__ */ jsx5(ConnectedModalContent, { onClose: closeModal }) : /* @__PURE__ */ jsx5(ConnectModalContent, { appIcon, appName, onClose: closeModal })
|
|
755
|
+
}
|
|
756
|
+
)
|
|
757
|
+
] });
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// src/PhantomProvider.tsx
|
|
761
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
762
|
+
function PhantomProvider({ children, config, debugConfig, theme, appIcon, appName }) {
|
|
763
|
+
const memoizedConfig = useMemo3(() => config, [config]);
|
|
764
|
+
const resolvedTheme = useMemo3(() => mergeTheme(theme || darkTheme), [theme]);
|
|
765
|
+
const [sdk, setSdk] = useState8(null);
|
|
766
|
+
const [isClient, setIsClient] = useState8(false);
|
|
767
|
+
const [isConnected, setIsConnected] = useState8(false);
|
|
768
|
+
const [isConnecting, setIsConnecting] = useState8(false);
|
|
769
|
+
const [isLoading, setIsLoading] = useState8(true);
|
|
770
|
+
const [errors, setErrors] = useState8({});
|
|
771
|
+
const [addresses, setAddresses] = useState8([]);
|
|
772
|
+
const [user, setUser] = useState8(null);
|
|
773
|
+
useEffect5(() => {
|
|
20
774
|
setIsClient(true);
|
|
21
775
|
}, []);
|
|
22
|
-
|
|
776
|
+
useEffect5(() => {
|
|
23
777
|
if (!isClient)
|
|
24
778
|
return;
|
|
25
779
|
const sdkInstance = new BrowserSDK(memoizedConfig);
|
|
26
780
|
setSdk(sdkInstance);
|
|
27
|
-
return () => {
|
|
28
|
-
sdkInstance.disconnect().catch(() => {
|
|
29
|
-
});
|
|
30
|
-
};
|
|
31
781
|
}, [isClient, memoizedConfig]);
|
|
32
|
-
|
|
782
|
+
useEffect5(() => {
|
|
33
783
|
if (!sdk)
|
|
34
784
|
return;
|
|
35
785
|
const handleConnectStart = () => {
|
|
36
786
|
setIsConnecting(true);
|
|
37
|
-
|
|
787
|
+
setErrors((prev) => ({ ...prev, connect: void 0 }));
|
|
38
788
|
};
|
|
39
|
-
const handleConnect = async () => {
|
|
789
|
+
const handleConnect = async (data) => {
|
|
40
790
|
try {
|
|
41
791
|
setIsConnected(true);
|
|
42
792
|
setIsConnecting(false);
|
|
43
|
-
|
|
44
|
-
setCurrentProviderType(providerInfo?.type || null);
|
|
793
|
+
setUser(data);
|
|
45
794
|
const addrs = await sdk.getAddresses();
|
|
46
795
|
setAddresses(addrs);
|
|
47
|
-
setWalletId(sdk.getWalletId());
|
|
48
796
|
} catch (err) {
|
|
49
797
|
console.error("Error connecting:", err);
|
|
50
798
|
try {
|
|
@@ -57,127 +805,91 @@ function PhantomProvider({ children, config, debugConfig }) {
|
|
|
57
805
|
const handleConnectError = (errorData) => {
|
|
58
806
|
setIsConnecting(false);
|
|
59
807
|
setIsConnected(false);
|
|
60
|
-
|
|
808
|
+
const isAutoConnectNoSession = errorData.source === "auto-connect" && (errorData.error === "No valid session found" || errorData.error === "No trusted connections available");
|
|
809
|
+
if (isAutoConnectNoSession) {
|
|
810
|
+
setErrors((prev) => ({ ...prev, connect: void 0 }));
|
|
811
|
+
} else {
|
|
812
|
+
setErrors((prev) => ({ ...prev, connect: new Error(errorData.error || "Connection failed") }));
|
|
813
|
+
}
|
|
814
|
+
setAddresses([]);
|
|
61
815
|
};
|
|
62
816
|
const handleDisconnect = () => {
|
|
63
817
|
setIsConnected(false);
|
|
64
818
|
setIsConnecting(false);
|
|
65
|
-
|
|
819
|
+
setErrors({});
|
|
66
820
|
setAddresses([]);
|
|
67
|
-
|
|
821
|
+
setUser(null);
|
|
822
|
+
};
|
|
823
|
+
const handleSpendingLimitReached = () => {
|
|
824
|
+
setErrors((prev) => ({ ...prev, spendingLimit: true }));
|
|
68
825
|
};
|
|
69
826
|
sdk.on("connect_start", handleConnectStart);
|
|
70
827
|
sdk.on("connect", handleConnect);
|
|
71
828
|
sdk.on("connect_error", handleConnectError);
|
|
72
829
|
sdk.on("disconnect", handleDisconnect);
|
|
830
|
+
sdk.on("spending_limit_reached", handleSpendingLimitReached);
|
|
73
831
|
return () => {
|
|
74
832
|
sdk.off("connect_start", handleConnectStart);
|
|
75
833
|
sdk.off("connect", handleConnect);
|
|
76
834
|
sdk.off("connect_error", handleConnectError);
|
|
77
835
|
sdk.off("disconnect", handleDisconnect);
|
|
836
|
+
sdk.off("spending_limit_reached", handleSpendingLimitReached);
|
|
78
837
|
};
|
|
79
838
|
}, [sdk]);
|
|
80
|
-
|
|
839
|
+
useEffect5(() => {
|
|
81
840
|
if (!debugConfig || !sdk)
|
|
82
841
|
return;
|
|
83
842
|
sdk.configureDebug(debugConfig);
|
|
84
843
|
}, [sdk, debugConfig]);
|
|
85
|
-
|
|
844
|
+
useEffect5(() => {
|
|
86
845
|
if (!isClient || !sdk)
|
|
87
846
|
return;
|
|
88
847
|
const initialize = async () => {
|
|
89
848
|
try {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
console.error("Error checking Phantom extension:", err);
|
|
94
|
-
setIsPhantomAvailable(false);
|
|
95
|
-
}
|
|
96
|
-
if (memoizedConfig.autoConnect !== false) {
|
|
97
|
-
sdk.autoConnect().catch(() => {
|
|
98
|
-
});
|
|
849
|
+
await sdk.autoConnect();
|
|
850
|
+
} catch (error) {
|
|
851
|
+
console.error("Auto-connect error:", error);
|
|
99
852
|
}
|
|
853
|
+
setIsLoading(false);
|
|
100
854
|
};
|
|
101
855
|
initialize();
|
|
102
|
-
}, [sdk,
|
|
103
|
-
const
|
|
856
|
+
}, [sdk, isClient]);
|
|
857
|
+
const clearError = useCallback6((key) => {
|
|
858
|
+
setErrors((prev) => {
|
|
859
|
+
const next = { ...prev };
|
|
860
|
+
delete next[key];
|
|
861
|
+
return next;
|
|
862
|
+
});
|
|
863
|
+
}, []);
|
|
864
|
+
const value = useMemo3(
|
|
104
865
|
() => ({
|
|
105
866
|
sdk,
|
|
106
867
|
isConnected,
|
|
107
868
|
isConnecting,
|
|
108
|
-
|
|
869
|
+
isLoading,
|
|
870
|
+
errors,
|
|
109
871
|
addresses,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
872
|
+
isClient,
|
|
873
|
+
user,
|
|
874
|
+
theme: resolvedTheme,
|
|
875
|
+
allowedProviders: memoizedConfig.providers,
|
|
876
|
+
clearError
|
|
114
877
|
}),
|
|
115
|
-
[
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
import { useCallback } from "react";
|
|
129
|
-
function useConnect() {
|
|
130
|
-
const { sdk, isConnecting, connectError, currentProviderType, isPhantomAvailable } = usePhantom();
|
|
131
|
-
const connect = useCallback(
|
|
132
|
-
async (options) => {
|
|
133
|
-
if (!sdk) {
|
|
134
|
-
throw new Error("SDK not initialized");
|
|
135
|
-
}
|
|
136
|
-
try {
|
|
137
|
-
const result = await sdk.connect(options);
|
|
138
|
-
return result;
|
|
139
|
-
} catch (err) {
|
|
140
|
-
console.error("Error connecting to Phantom:", err);
|
|
141
|
-
throw err;
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
[sdk]
|
|
878
|
+
[
|
|
879
|
+
sdk,
|
|
880
|
+
isConnected,
|
|
881
|
+
isConnecting,
|
|
882
|
+
isLoading,
|
|
883
|
+
errors,
|
|
884
|
+
addresses,
|
|
885
|
+
isClient,
|
|
886
|
+
user,
|
|
887
|
+
resolvedTheme,
|
|
888
|
+
memoizedConfig.providers,
|
|
889
|
+
clearError
|
|
890
|
+
]
|
|
145
891
|
);
|
|
146
|
-
return {
|
|
147
|
-
connect,
|
|
148
|
-
isConnecting,
|
|
149
|
-
error: connectError,
|
|
150
|
-
currentProviderType,
|
|
151
|
-
isPhantomAvailable
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// src/hooks/useDisconnect.ts
|
|
156
|
-
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
157
|
-
function useDisconnect() {
|
|
158
|
-
const { sdk } = usePhantom();
|
|
159
|
-
const [isDisconnecting, setIsDisconnecting] = useState2(false);
|
|
160
|
-
const [error, setError] = useState2(null);
|
|
161
|
-
const disconnect = useCallback2(async () => {
|
|
162
|
-
if (!sdk) {
|
|
163
|
-
throw new Error("SDK not initialized");
|
|
164
|
-
}
|
|
165
|
-
setIsDisconnecting(true);
|
|
166
|
-
setError(null);
|
|
167
|
-
try {
|
|
168
|
-
await sdk.disconnect();
|
|
169
|
-
} catch (err) {
|
|
170
|
-
setError(err);
|
|
171
|
-
throw err;
|
|
172
|
-
} finally {
|
|
173
|
-
setIsDisconnecting(false);
|
|
174
|
-
}
|
|
175
|
-
}, [sdk]);
|
|
176
|
-
return {
|
|
177
|
-
disconnect,
|
|
178
|
-
isDisconnecting,
|
|
179
|
-
error
|
|
180
|
-
};
|
|
892
|
+
return /* @__PURE__ */ jsx6(ThemeProvider, { theme: resolvedTheme, children: /* @__PURE__ */ jsx6(PhantomContext.Provider, { value, children: /* @__PURE__ */ jsx6(ModalProvider, { appIcon, appName, children }) }) });
|
|
181
893
|
}
|
|
182
894
|
|
|
183
895
|
// src/hooks/useAccounts.ts
|
|
@@ -186,49 +898,16 @@ function useAccounts() {
|
|
|
186
898
|
return isConnected ? addresses : null;
|
|
187
899
|
}
|
|
188
900
|
|
|
189
|
-
// src/hooks/useIsExtensionInstalled.ts
|
|
190
|
-
import * as React from "react";
|
|
191
|
-
import { waitForPhantomExtension } from "@phantom/browser-sdk";
|
|
192
|
-
function useIsExtensionInstalled() {
|
|
193
|
-
const [isLoading, setIsLoading] = React.useState(true);
|
|
194
|
-
const [isInstalled, setIsInstalled] = React.useState(false);
|
|
195
|
-
React.useEffect(() => {
|
|
196
|
-
let isMounted = true;
|
|
197
|
-
const checkExtension = async () => {
|
|
198
|
-
try {
|
|
199
|
-
setIsLoading(true);
|
|
200
|
-
const result = await waitForPhantomExtension(3e3);
|
|
201
|
-
if (isMounted) {
|
|
202
|
-
setIsInstalled(result);
|
|
203
|
-
}
|
|
204
|
-
} catch (error) {
|
|
205
|
-
if (isMounted) {
|
|
206
|
-
setIsInstalled(false);
|
|
207
|
-
}
|
|
208
|
-
} finally {
|
|
209
|
-
if (isMounted) {
|
|
210
|
-
setIsLoading(false);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
checkExtension();
|
|
215
|
-
return () => {
|
|
216
|
-
isMounted = false;
|
|
217
|
-
};
|
|
218
|
-
}, []);
|
|
219
|
-
return { isLoading, isInstalled };
|
|
220
|
-
}
|
|
221
|
-
|
|
222
901
|
// src/hooks/useAutoConfirm.ts
|
|
223
|
-
import { useCallback as
|
|
902
|
+
import { useCallback as useCallback7, useState as useState9, useEffect as useEffect6 } from "react";
|
|
224
903
|
function useAutoConfirm() {
|
|
225
|
-
const { sdk,
|
|
226
|
-
const [status, setStatus] =
|
|
227
|
-
const [supportedChains, setSupportedChains] =
|
|
228
|
-
const [isLoading, setIsLoading] =
|
|
229
|
-
const [error, setError] =
|
|
230
|
-
const isInjected =
|
|
231
|
-
const enable =
|
|
904
|
+
const { sdk, user } = usePhantom();
|
|
905
|
+
const [status, setStatus] = useState9(null);
|
|
906
|
+
const [supportedChains, setSupportedChains] = useState9(null);
|
|
907
|
+
const [isLoading, setIsLoading] = useState9(false);
|
|
908
|
+
const [error, setError] = useState9(null);
|
|
909
|
+
const isInjected = user?.authProvider === "injected";
|
|
910
|
+
const enable = useCallback7(
|
|
232
911
|
async (params) => {
|
|
233
912
|
if (!sdk) {
|
|
234
913
|
throw new Error("SDK not initialized");
|
|
@@ -252,7 +931,7 @@ function useAutoConfirm() {
|
|
|
252
931
|
},
|
|
253
932
|
[sdk, isInjected]
|
|
254
933
|
);
|
|
255
|
-
const disable =
|
|
934
|
+
const disable = useCallback7(async () => {
|
|
256
935
|
if (!sdk) {
|
|
257
936
|
throw new Error("SDK not initialized");
|
|
258
937
|
}
|
|
@@ -273,7 +952,7 @@ function useAutoConfirm() {
|
|
|
273
952
|
setIsLoading(false);
|
|
274
953
|
}
|
|
275
954
|
}, [sdk, isInjected]);
|
|
276
|
-
const refetch =
|
|
955
|
+
const refetch = useCallback7(async () => {
|
|
277
956
|
if (!sdk || !isInjected) {
|
|
278
957
|
return;
|
|
279
958
|
}
|
|
@@ -293,7 +972,7 @@ function useAutoConfirm() {
|
|
|
293
972
|
setIsLoading(false);
|
|
294
973
|
}
|
|
295
974
|
}, [sdk, isInjected]);
|
|
296
|
-
|
|
975
|
+
useEffect6(() => {
|
|
297
976
|
if (sdk && isInjected) {
|
|
298
977
|
refetch();
|
|
299
978
|
} else {
|
|
@@ -314,55 +993,175 @@ function useAutoConfirm() {
|
|
|
314
993
|
}
|
|
315
994
|
|
|
316
995
|
// src/hooks/useSolana.ts
|
|
996
|
+
import { AddressType } from "@phantom/browser-sdk";
|
|
317
997
|
function useSolana() {
|
|
318
|
-
const { sdk,
|
|
319
|
-
if (!isClient || !sdk) {
|
|
998
|
+
const { sdk, isClient, isLoading } = usePhantom();
|
|
999
|
+
if (!isClient || !sdk || isLoading) {
|
|
1000
|
+
return {
|
|
1001
|
+
solana: {},
|
|
1002
|
+
isAvailable: false
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
const enabledAddressTypes = sdk.getEnabledAddressTypes();
|
|
1006
|
+
const isAvailable = enabledAddressTypes.includes(AddressType.solana);
|
|
1007
|
+
if (!isAvailable) {
|
|
1008
|
+
return {
|
|
1009
|
+
solana: {},
|
|
1010
|
+
isAvailable: false
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
try {
|
|
1014
|
+
return {
|
|
1015
|
+
solana: sdk.solana,
|
|
1016
|
+
isAvailable: true
|
|
1017
|
+
};
|
|
1018
|
+
} catch (error) {
|
|
320
1019
|
return {
|
|
321
1020
|
solana: {},
|
|
322
|
-
// This will be replaced when SDK is ready
|
|
323
1021
|
isAvailable: false
|
|
324
1022
|
};
|
|
325
1023
|
}
|
|
326
|
-
return {
|
|
327
|
-
// Chain instance with connection enforcement for signing methods
|
|
328
|
-
solana: sdk.solana,
|
|
329
|
-
// State
|
|
330
|
-
isAvailable: !!isConnected
|
|
331
|
-
};
|
|
332
1024
|
}
|
|
333
1025
|
|
|
334
1026
|
// src/hooks/useEthereum.ts
|
|
1027
|
+
import { AddressType as AddressType2 } from "@phantom/browser-sdk";
|
|
335
1028
|
function useEthereum() {
|
|
336
|
-
const { sdk,
|
|
337
|
-
if (!isClient || !sdk) {
|
|
1029
|
+
const { sdk, isClient, isLoading } = usePhantom();
|
|
1030
|
+
if (!isClient || !sdk || isLoading) {
|
|
338
1031
|
return {
|
|
339
1032
|
ethereum: {},
|
|
340
|
-
// This will be replaced when SDK is ready
|
|
341
1033
|
isAvailable: false
|
|
342
1034
|
};
|
|
343
1035
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
1036
|
+
const enabledAddressTypes = sdk.getEnabledAddressTypes();
|
|
1037
|
+
const isAvailable = enabledAddressTypes.includes(AddressType2.ethereum);
|
|
1038
|
+
if (!isAvailable) {
|
|
1039
|
+
return {
|
|
1040
|
+
ethereum: {},
|
|
1041
|
+
isAvailable: false
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
try {
|
|
1045
|
+
return {
|
|
1046
|
+
ethereum: sdk.ethereum,
|
|
1047
|
+
isAvailable: true
|
|
1048
|
+
};
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
return {
|
|
1051
|
+
ethereum: {},
|
|
1052
|
+
isAvailable: false
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// src/hooks/index.ts
|
|
1058
|
+
import { useTheme as useTheme5 } from "@phantom/wallet-sdk-ui";
|
|
1059
|
+
|
|
1060
|
+
// src/components/ConnectButton.tsx
|
|
1061
|
+
import { useMemo as useMemo4 } from "react";
|
|
1062
|
+
import { useTheme as useTheme6 } from "@phantom/wallet-sdk-ui";
|
|
1063
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1064
|
+
function ConnectButton({ addressType, fullWidth = false }) {
|
|
1065
|
+
const theme = useTheme6();
|
|
1066
|
+
const { open } = useModal();
|
|
1067
|
+
const { isConnected, addresses } = usePhantom();
|
|
1068
|
+
const displayAddress = useMemo4(() => {
|
|
1069
|
+
if (!addresses || addresses.length === 0)
|
|
1070
|
+
return null;
|
|
1071
|
+
if (addressType) {
|
|
1072
|
+
return addresses.find((addr) => addr.addressType === addressType);
|
|
1073
|
+
}
|
|
1074
|
+
return addresses[0];
|
|
1075
|
+
}, [addresses, addressType]);
|
|
1076
|
+
const truncatedAddress = useMemo4(() => {
|
|
1077
|
+
if (!displayAddress)
|
|
1078
|
+
return "";
|
|
1079
|
+
const addr = displayAddress.address;
|
|
1080
|
+
if (addr.length <= 12)
|
|
1081
|
+
return addr;
|
|
1082
|
+
return `${addr.slice(0, 6)}...${addr.slice(-4)}`;
|
|
1083
|
+
}, [displayAddress]);
|
|
1084
|
+
const buttonStyle = {
|
|
1085
|
+
width: fullWidth ? "100%" : "auto",
|
|
1086
|
+
padding: "12px 16px",
|
|
1087
|
+
border: "none",
|
|
1088
|
+
borderRadius: theme.borderRadius,
|
|
1089
|
+
fontFamily: theme.typography.captionBold.fontFamily,
|
|
1090
|
+
fontSize: theme.typography.captionBold.fontSize,
|
|
1091
|
+
fontStyle: theme.typography.captionBold.fontStyle,
|
|
1092
|
+
fontWeight: theme.typography.captionBold.fontWeight,
|
|
1093
|
+
lineHeight: theme.typography.captionBold.lineHeight,
|
|
1094
|
+
letterSpacing: theme.typography.captionBold.letterSpacing,
|
|
1095
|
+
cursor: "pointer",
|
|
1096
|
+
transition: "background-color 0.2s",
|
|
1097
|
+
display: "flex",
|
|
1098
|
+
alignItems: "center",
|
|
1099
|
+
justifyContent: "center",
|
|
1100
|
+
gap: "8px",
|
|
1101
|
+
background: theme.aux,
|
|
1102
|
+
color: theme.text
|
|
1103
|
+
};
|
|
1104
|
+
const connectedButtonStyle = {
|
|
1105
|
+
...buttonStyle,
|
|
1106
|
+
background: theme.aux,
|
|
1107
|
+
cursor: "pointer"
|
|
1108
|
+
};
|
|
1109
|
+
if (isConnected && displayAddress) {
|
|
1110
|
+
return /* @__PURE__ */ jsx7("button", { style: connectedButtonStyle, onClick: open, children: /* @__PURE__ */ jsx7("span", { style: { fontFamily: "monospace" }, children: truncatedAddress }) });
|
|
1111
|
+
}
|
|
1112
|
+
return /* @__PURE__ */ jsx7("button", { style: buttonStyle, onClick: open, children: "Connect Wallet" });
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// src/components/ConnectBox.tsx
|
|
1116
|
+
import { useMemo as useMemo5 } from "react";
|
|
1117
|
+
import { useTheme as useTheme7 } from "@phantom/wallet-sdk-ui";
|
|
1118
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1119
|
+
function ConnectBox({ maxWidth = "350px", transparent = false, appIcon, appName }) {
|
|
1120
|
+
const theme = useTheme7();
|
|
1121
|
+
const { isConnected } = usePhantom();
|
|
1122
|
+
const boxStyle = useMemo5(() => {
|
|
1123
|
+
const style = {
|
|
1124
|
+
width: "100%",
|
|
1125
|
+
maxWidth: typeof maxWidth === "number" ? `${maxWidth}px` : maxWidth,
|
|
1126
|
+
position: "relative",
|
|
1127
|
+
overflow: "hidden"
|
|
1128
|
+
};
|
|
1129
|
+
if (!transparent) {
|
|
1130
|
+
style.backgroundColor = theme.background;
|
|
1131
|
+
style.borderRadius = theme.borderRadius;
|
|
1132
|
+
}
|
|
1133
|
+
return style;
|
|
1134
|
+
}, [maxWidth, transparent, theme.background, theme.borderRadius]);
|
|
1135
|
+
const noOp = () => {
|
|
349
1136
|
};
|
|
1137
|
+
return /* @__PURE__ */ jsx8("div", { style: boxStyle, children: isConnected ? /* @__PURE__ */ jsx8(ConnectedModalContent, { onClose: noOp, hideCloseButton: true }) : /* @__PURE__ */ jsx8(ConnectModalContent, { appIcon, appName, onClose: noOp, hideCloseButton: true }) });
|
|
350
1138
|
}
|
|
351
1139
|
|
|
352
1140
|
// src/index.ts
|
|
353
|
-
import {
|
|
1141
|
+
import { darkTheme as darkTheme2, lightTheme, mergeTheme as mergeTheme2 } from "@phantom/wallet-sdk-ui";
|
|
1142
|
+
import { NetworkId, AddressType as AddressType3, DebugLevel, debug, isMobileDevice as isMobileDevice3 } from "@phantom/browser-sdk";
|
|
354
1143
|
export {
|
|
355
|
-
AddressType,
|
|
1144
|
+
AddressType3 as AddressType,
|
|
1145
|
+
ConnectBox,
|
|
1146
|
+
ConnectButton,
|
|
356
1147
|
DebugLevel,
|
|
357
1148
|
NetworkId,
|
|
358
1149
|
PhantomProvider,
|
|
1150
|
+
darkTheme2 as darkTheme,
|
|
359
1151
|
debug,
|
|
1152
|
+
isMobileDevice3 as isMobileDevice,
|
|
1153
|
+
lightTheme,
|
|
1154
|
+
mergeTheme2 as mergeTheme,
|
|
360
1155
|
useAccounts,
|
|
361
1156
|
useAutoConfirm,
|
|
362
1157
|
useConnect,
|
|
363
1158
|
useDisconnect,
|
|
1159
|
+
useDiscoveredWallets,
|
|
364
1160
|
useEthereum,
|
|
365
1161
|
useIsExtensionInstalled,
|
|
1162
|
+
useIsPhantomLoginAvailable,
|
|
1163
|
+
useModal,
|
|
366
1164
|
usePhantom,
|
|
367
|
-
useSolana
|
|
1165
|
+
useSolana,
|
|
1166
|
+
useTheme5 as useTheme
|
|
368
1167
|
};
|