@phantom/react-native-sdk 1.0.0-beta.9 → 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/dist/index.js CHANGED
@@ -33,25 +33,430 @@ __export(src_exports, {
33
33
  AddressType: () => import_client.AddressType,
34
34
  NetworkId: () => import_constants3.NetworkId,
35
35
  PhantomProvider: () => PhantomProvider,
36
+ darkTheme: () => import_wallet_sdk_ui6.darkTheme,
37
+ lightTheme: () => import_wallet_sdk_ui6.lightTheme,
36
38
  useAccounts: () => useAccounts,
37
39
  useConnect: () => useConnect,
38
40
  useDisconnect: () => useDisconnect,
39
41
  useEthereum: () => useEthereum,
42
+ useModal: () => useModal,
40
43
  usePhantom: () => usePhantom,
41
44
  useSolana: () => useSolana
42
45
  });
43
46
  module.exports = __toCommonJS(src_exports);
44
47
 
45
48
  // src/PhantomProvider.tsx
46
- var import_react = require("react");
49
+ var import_react8 = require("react");
47
50
  var import_embedded_provider_core = require("@phantom/embedded-provider-core");
48
51
  var import_constants2 = require("@phantom/constants");
52
+ var import_wallet_sdk_ui5 = require("@phantom/wallet-sdk-ui");
53
+
54
+ // src/ModalProvider.tsx
55
+ var import_react7 = require("react");
56
+
57
+ // src/ModalContext.ts
58
+ var import_react = require("react");
59
+ var ModalContext = (0, import_react.createContext)(void 0);
60
+ function useModal() {
61
+ const context = (0, import_react.useContext)(ModalContext);
62
+ if (!context) {
63
+ throw new Error("useModal must be used within a ModalProvider");
64
+ }
65
+ return {
66
+ open: context.openModal,
67
+ close: context.closeModal,
68
+ isOpened: context.isModalOpen
69
+ };
70
+ }
71
+
72
+ // src/components/Modal.tsx
73
+ var import_react_native = require("react-native");
74
+ var import_wallet_sdk_ui = require("@phantom/wallet-sdk-ui");
75
+ var import_jsx_runtime = require("react/jsx-runtime");
76
+ function Modal({ isVisible, onClose, children }) {
77
+ const theme = (0, import_wallet_sdk_ui.useTheme)();
78
+ const styles = import_react_native.StyleSheet.create({
79
+ bottomSheet: {
80
+ backgroundColor: theme.background,
81
+ borderTopLeftRadius: 32,
82
+ borderTopRightRadius: 32,
83
+ bottom: 0,
84
+ left: 0,
85
+ paddingBottom: 20,
86
+ position: "absolute",
87
+ right: 0
88
+ },
89
+ handle: {
90
+ alignSelf: "center",
91
+ backgroundColor: theme.secondary,
92
+ borderRadius: 2.5,
93
+ height: 5,
94
+ marginTop: 12,
95
+ opacity: 0.3,
96
+ width: 40
97
+ }
98
+ });
99
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Modal, { visible: isVisible, transparent: true, animationType: "slide", onRequestClose: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.SafeAreaView, { style: styles.bottomSheet, children: [
100
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: styles.handle }),
101
+ children
102
+ ] }) });
103
+ }
104
+
105
+ // src/PhantomContext.tsx
106
+ var import_react2 = require("react");
107
+ var PhantomContext = (0, import_react2.createContext)(void 0);
108
+ function usePhantom() {
109
+ const context = (0, import_react2.useContext)(PhantomContext);
110
+ if (context === void 0) {
111
+ throw new Error("usePhantom must be used within a PhantomProvider");
112
+ }
113
+ return context;
114
+ }
115
+
116
+ // src/components/ConnectModalContent.tsx
117
+ var import_react4 = require("react");
118
+ var import_react_native2 = require("react-native");
119
+ var import_wallet_sdk_ui2 = require("@phantom/wallet-sdk-ui");
120
+
121
+ // src/hooks/useConnect.ts
122
+ var import_react3 = require("react");
123
+ function useConnect() {
124
+ const { sdk, isConnecting, errors, setWalletId } = usePhantom();
125
+ const connect = (0, import_react3.useCallback)(
126
+ async (options) => {
127
+ if (!sdk) {
128
+ throw new Error("SDK not initialized");
129
+ }
130
+ try {
131
+ const result = await sdk.connect(options);
132
+ if (result.status === "completed" && result.walletId) {
133
+ setWalletId(result.walletId);
134
+ }
135
+ return result;
136
+ } catch (err) {
137
+ const error = err;
138
+ throw error;
139
+ }
140
+ },
141
+ [sdk, setWalletId]
142
+ );
143
+ return {
144
+ connect,
145
+ isConnecting,
146
+ error: errors.connect
147
+ };
148
+ }
149
+
150
+ // src/components/ConnectModalContent.tsx
151
+ var import_jsx_runtime2 = require("react/jsx-runtime");
152
+ function ConnectModalContent({ appIcon, onClose }) {
153
+ const theme = (0, import_wallet_sdk_ui2.useTheme)();
154
+ const { isConnecting: contextIsConnecting, allowedProviders } = usePhantom();
155
+ const { connect } = useConnect();
156
+ const [isConnecting, setIsConnecting] = (0, import_react4.useState)(false);
157
+ const [error, setError] = (0, import_react4.useState)(null);
158
+ const [providerType, setProviderType] = (0, import_react4.useState)(null);
159
+ const isLoading = contextIsConnecting || isConnecting;
160
+ const errorBackgroundColor = (0, import_wallet_sdk_ui2.hexToRgba)(theme.error, 0.1);
161
+ const errorBorderColor = (0, import_wallet_sdk_ui2.hexToRgba)(theme.error, 0.3);
162
+ const errorTextColor = theme.error;
163
+ const connectWithAuthProvider = (0, import_react4.useCallback)(
164
+ async (provider) => {
165
+ try {
166
+ setIsConnecting(true);
167
+ setError(null);
168
+ setProviderType(provider);
169
+ await connect({ provider });
170
+ onClose();
171
+ } catch (err) {
172
+ const error2 = err instanceof Error ? err : new Error(String(err));
173
+ setError(error2);
174
+ } finally {
175
+ setIsConnecting(false);
176
+ setProviderType(null);
177
+ }
178
+ },
179
+ [connect, onClose]
180
+ );
181
+ const styles = import_react_native2.StyleSheet.create({
182
+ appIcon: {
183
+ borderRadius: 28,
184
+ height: 56,
185
+ marginBottom: 12,
186
+ width: 56
187
+ },
188
+ buttonContainer: {
189
+ alignItems: "center",
190
+ flexDirection: "column",
191
+ gap: 12,
192
+ paddingHorizontal: 32,
193
+ width: "100%"
194
+ },
195
+ buttonContent: {
196
+ alignItems: "center",
197
+ flexDirection: "row",
198
+ justifyContent: "space-between",
199
+ width: "100%"
200
+ },
201
+ buttonContentLeft: {
202
+ alignItems: "center",
203
+ flexDirection: "row",
204
+ gap: 8
205
+ },
206
+ container: {
207
+ alignItems: "center",
208
+ flexDirection: "column",
209
+ gap: 12,
210
+ paddingBottom: 24,
211
+ width: "100%"
212
+ },
213
+ errorContainer: {
214
+ backgroundColor: errorBackgroundColor,
215
+ borderColor: errorBorderColor,
216
+ borderRadius: parseInt(theme.borderRadius),
217
+ borderWidth: 1,
218
+ padding: 12,
219
+ width: "100%"
220
+ },
221
+ errorText: {
222
+ color: errorTextColor,
223
+ fontSize: 14
224
+ },
225
+ footer: {
226
+ alignItems: "center",
227
+ borderColor: theme.aux,
228
+ borderTopWidth: 1,
229
+ flexDirection: "row",
230
+ gap: 4,
231
+ justifyContent: "center",
232
+ marginTop: 24,
233
+ padding: 16,
234
+ width: "100%"
235
+ },
236
+ loadingContainer: {
237
+ alignItems: "center",
238
+ flexDirection: "column",
239
+ gap: 12,
240
+ justifyContent: "center",
241
+ padding: 24
242
+ }
243
+ });
244
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles.container, children: [
245
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.ModalHeader, { title: "Login or Sign Up", onClose }),
246
+ appIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Image, { testID: "app-icon", source: { uri: appIcon }, style: styles.appIcon }),
247
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles.loadingContainer, children: [
248
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.ActivityIndicator, { testID: "activity-indicator", size: "large", color: theme.brand }),
249
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.Text, { variant: "label", color: theme.secondary, children: "Loading..." })
250
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles.buttonContainer, children: [
251
+ error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles.errorContainer, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.Text, { style: styles.errorText, children: error.message }) }),
252
+ allowedProviders.includes("google") && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
253
+ import_wallet_sdk_ui2.Button,
254
+ {
255
+ onClick: () => connectWithAuthProvider("google"),
256
+ disabled: isConnecting,
257
+ isLoading: isConnecting && providerType === "google",
258
+ fullWidth: true,
259
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles.buttonContent, children: [
260
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles.buttonContentLeft, children: [
261
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.Icon, { type: "google", size: 20, color: theme.text }),
262
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.Text, { variant: "captionBold", children: "Continue with Google" })
263
+ ] }),
264
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.Icon, { type: "chevron-right", size: 16, color: theme.secondary })
265
+ ] })
266
+ }
267
+ ),
268
+ allowedProviders.includes("apple") && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
269
+ import_wallet_sdk_ui2.Button,
270
+ {
271
+ onClick: () => connectWithAuthProvider("apple"),
272
+ disabled: isConnecting,
273
+ isLoading: isConnecting && providerType === "apple",
274
+ fullWidth: true,
275
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles.buttonContent, children: [
276
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles.buttonContentLeft, children: [
277
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.Icon, { type: "apple", size: 20, color: theme.text }),
278
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.Text, { variant: "captionBold", children: "Continue with Apple" })
279
+ ] }),
280
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.Icon, { type: "chevron-right", size: 16, color: theme.secondary })
281
+ ] })
282
+ }
283
+ )
284
+ ] }),
285
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles.footer, children: [
286
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.Text, { variant: "label", color: theme.secondary, children: "Powered by" }),
287
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.Icon, { type: "phantom", size: 16, color: theme.secondary }),
288
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_wallet_sdk_ui2.Text, { variant: "label", color: theme.secondary, children: "Phantom" })
289
+ ] })
290
+ ] });
291
+ }
292
+
293
+ // src/components/ConnectedModalContent.tsx
294
+ var import_react6 = require("react");
295
+ var import_react_native3 = require("react-native");
296
+ var import_wallet_sdk_ui3 = require("@phantom/wallet-sdk-ui");
297
+
298
+ // src/hooks/useDisconnect.ts
299
+ var import_react5 = require("react");
300
+ function useDisconnect() {
301
+ const { sdk } = usePhantom();
302
+ const [isDisconnecting, setIsDisconnecting] = (0, import_react5.useState)(false);
303
+ const [error, setError] = (0, import_react5.useState)(null);
304
+ const disconnect = (0, import_react5.useCallback)(async () => {
305
+ if (!sdk) {
306
+ throw new Error("SDK not initialized");
307
+ }
308
+ setIsDisconnecting(true);
309
+ setError(null);
310
+ try {
311
+ await sdk.disconnect();
312
+ } catch (err) {
313
+ const error2 = err;
314
+ setError(error2);
315
+ throw error2;
316
+ } finally {
317
+ setIsDisconnecting(false);
318
+ }
319
+ }, [sdk]);
320
+ return {
321
+ disconnect,
322
+ isDisconnecting,
323
+ error
324
+ };
325
+ }
326
+
327
+ // src/components/ConnectedModalContent.tsx
328
+ var import_jsx_runtime3 = require("react/jsx-runtime");
329
+ function ConnectedModalContent({ onClose }) {
330
+ const theme = (0, import_wallet_sdk_ui3.useTheme)();
331
+ const { addresses } = usePhantom();
332
+ const { disconnect } = useDisconnect();
333
+ const [isDisconnecting, setIsDisconnecting] = (0, import_react6.useState)(false);
334
+ const [disconnectError, setDisconnectError] = (0, import_react6.useState)(null);
335
+ const errorBackgroundColor = (0, import_wallet_sdk_ui3.hexToRgba)(theme.error, 0.1);
336
+ const errorBorderColor = (0, import_wallet_sdk_ui3.hexToRgba)(theme.error, 0.3);
337
+ (0, import_react6.useEffect)(() => {
338
+ setDisconnectError(null);
339
+ }, []);
340
+ const handleDisconnect = async () => {
341
+ try {
342
+ setIsDisconnecting(true);
343
+ setDisconnectError(null);
344
+ await disconnect();
345
+ onClose();
346
+ } catch (err) {
347
+ const error = err instanceof Error ? err : new Error(String(err));
348
+ setDisconnectError(error);
349
+ } finally {
350
+ setIsDisconnecting(false);
351
+ }
352
+ };
353
+ const styles = import_react_native3.StyleSheet.create({
354
+ accountItem: {
355
+ flexDirection: "column",
356
+ gap: 8,
357
+ width: "100%"
358
+ },
359
+ accountList: {
360
+ flexDirection: "column",
361
+ gap: 16,
362
+ width: "100%"
363
+ },
364
+ accountTypeText: {
365
+ textTransform: "uppercase"
366
+ },
367
+ addressText: {
368
+ fontFamily: "monospace"
369
+ },
370
+ container: {
371
+ alignItems: "center",
372
+ flexDirection: "column",
373
+ gap: 24,
374
+ paddingBottom: 24,
375
+ paddingHorizontal: 32,
376
+ width: "100%"
377
+ },
378
+ errorContainer: {
379
+ backgroundColor: errorBackgroundColor,
380
+ borderColor: errorBorderColor,
381
+ borderRadius: theme.borderRadius,
382
+ borderWidth: 1,
383
+ padding: 12,
384
+ width: "100%"
385
+ }
386
+ });
387
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles.container, children: [
388
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_wallet_sdk_ui3.ModalHeader, { title: "Wallet", onClose }),
389
+ addresses && addresses.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: styles.accountList, children: addresses.map((account, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles.accountItem, children: [
390
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_wallet_sdk_ui3.Text, { variant: "label", color: theme.secondary, style: styles.accountTypeText, children: account.addressType }),
391
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_wallet_sdk_ui3.Text, { variant: "caption", style: styles.addressText, children: account.address })
392
+ ] }, index)) }),
393
+ disconnectError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: styles.errorContainer, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_wallet_sdk_ui3.Text, { variant: "caption", color: theme.error, children: "Failed to disconnect" }) }),
394
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_wallet_sdk_ui3.Button, { onClick: handleDisconnect, disabled: isDisconnecting, isLoading: isDisconnecting, fullWidth: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_wallet_sdk_ui3.Text, { variant: "captionBold", children: isDisconnecting ? "Disconnecting..." : "Disconnect" }) })
395
+ ] });
396
+ }
397
+
398
+ // src/components/SpendingLimitModalContent.tsx
399
+ var import_react_native4 = require("react-native");
400
+ var import_wallet_sdk_ui4 = require("@phantom/wallet-sdk-ui");
401
+ var import_jsx_runtime4 = require("react/jsx-runtime");
402
+ function SpendingLimitModalContent({ onClose }) {
403
+ const theme = (0, import_wallet_sdk_ui4.useTheme)();
404
+ const styles = import_react_native4.StyleSheet.create({
405
+ container: {
406
+ flexDirection: "column",
407
+ gap: 16,
408
+ padding: 32
409
+ }
410
+ });
411
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles.container, children: [
412
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_wallet_sdk_ui4.Text, { variant: "caption", color: theme.secondary, children: "You've reached the maximum daily limit allowed to spend by this application." }),
413
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_wallet_sdk_ui4.Button, { fullWidth: true, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_wallet_sdk_ui4.Text, { variant: "captionBold", children: "Close" }) })
414
+ ] });
415
+ }
416
+
417
+ // src/ModalProvider.tsx
418
+ var import_jsx_runtime5 = require("react/jsx-runtime");
419
+ function ModalProvider({ children, appIcon, appName }) {
420
+ const { isConnected, errors, clearError } = usePhantom();
421
+ const [isModalOpen, setIsModalOpen] = (0, import_react7.useState)(false);
422
+ const openModal = (0, import_react7.useCallback)(() => {
423
+ setIsModalOpen(true);
424
+ }, []);
425
+ const closeModal = (0, import_react7.useCallback)(() => {
426
+ setIsModalOpen(false);
427
+ clearError("spendingLimit");
428
+ }, [clearError]);
429
+ const isSpendingLimitOpen = !!errors.spendingLimit;
430
+ const modalContextValue = (0, import_react7.useMemo)(
431
+ () => ({
432
+ isModalOpen,
433
+ openModal,
434
+ closeModal
435
+ }),
436
+ [isModalOpen, openModal, closeModal]
437
+ );
438
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ModalContext.Provider, { value: modalContextValue, children: [
439
+ children,
440
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
441
+ Modal,
442
+ {
443
+ isVisible: isModalOpen || isSpendingLimitOpen,
444
+ onClose: closeModal,
445
+ appIcon,
446
+ appName,
447
+ isMobile: true,
448
+ children: isSpendingLimitOpen ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SpendingLimitModalContent, { onClose: closeModal }) : isConnected ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ConnectedModalContent, { onClose: closeModal }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ConnectModalContent, { appIcon, appName, onClose: closeModal })
449
+ }
450
+ )
451
+ ] });
452
+ }
49
453
 
50
454
  // src/providers/embedded/storage.ts
51
455
  var SecureStore = __toESM(require("expo-secure-store"));
52
456
  var ExpoSecureStorage = class {
53
457
  constructor(requireAuth = false) {
54
458
  this.sessionKey = "phantom_session";
459
+ this.logoutFlagKey = "phantom_should_clear_previous_session";
55
460
  this.requireAuth = requireAuth;
56
461
  }
57
462
  async saveSession(session) {
@@ -86,6 +491,36 @@ var ExpoSecureStorage = class {
86
491
  console.error("[ExpoSecureStorage] Failed to clear session", { error: error.message });
87
492
  }
88
493
  }
494
+ async getShouldClearPreviousSession() {
495
+ try {
496
+ const flagData = await SecureStore.getItemAsync(this.logoutFlagKey, {
497
+ requireAuthentication: false
498
+ // Don't require auth for this flag
499
+ });
500
+ if (!flagData) {
501
+ return false;
502
+ }
503
+ return flagData === "true";
504
+ } catch (error) {
505
+ console.error("[ExpoSecureStorage] Failed to get shouldClearPreviousSession flag", {
506
+ error: error.message
507
+ });
508
+ return false;
509
+ }
510
+ }
511
+ async setShouldClearPreviousSession(should) {
512
+ try {
513
+ await SecureStore.setItemAsync(this.logoutFlagKey, should.toString(), {
514
+ requireAuthentication: false,
515
+ // Don't require auth for this flag
516
+ keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY
517
+ });
518
+ } catch (error) {
519
+ console.error("[ExpoSecureStorage] Failed to set shouldClearPreviousSession flag", {
520
+ error: error.message
521
+ });
522
+ }
523
+ }
89
524
  async isAvailable() {
90
525
  return await SecureStore.isAvailableAsync();
91
526
  }
@@ -97,6 +532,7 @@ var ExpoSecureStorage = class {
97
532
 
98
533
  // src/providers/embedded/auth.ts
99
534
  var WebBrowser = __toESM(require("expo-web-browser"));
535
+ var import_react_native5 = require("react-native");
100
536
  var import_constants = require("@phantom/constants");
101
537
  var ExpoAuthProvider = class {
102
538
  async authenticate(options) {
@@ -104,22 +540,26 @@ var ExpoAuthProvider = class {
104
540
  return;
105
541
  }
106
542
  const phantomOptions = options;
107
- const { authUrl, redirectUrl, organizationId, sessionId, provider, customAuthData, appId } = phantomOptions;
543
+ const { authUrl, redirectUrl, publicKey, sessionId, provider, appId } = phantomOptions;
108
544
  if (!redirectUrl) {
109
545
  throw new Error("redirectUrl is required for web browser authentication");
110
546
  }
111
- if (!organizationId || !sessionId || !appId) {
112
- throw new Error("organizationId, sessionId and appId are required for authentication");
547
+ if (!publicKey || !sessionId || !appId) {
548
+ throw new Error("publicKey, sessionId and appId are required for authentication");
113
549
  }
114
550
  try {
115
551
  const baseUrl = authUrl || import_constants.DEFAULT_AUTH_URL;
116
552
  const params = new URLSearchParams({
117
- organization_id: organizationId,
553
+ public_key: publicKey,
118
554
  app_id: appId,
119
555
  redirect_uri: redirectUrl,
120
556
  session_id: sessionId,
121
- clear_previous_session: "true",
122
- sdk_version: "1.0.0-beta.9"
557
+ // OAuth session management - defaults to allow refresh unless explicitly clearing after logout
558
+ clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
559
+ allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
560
+ sdk_version: "1.0.0",
561
+ sdk_type: "react-native",
562
+ platform: import_react_native5.Platform.OS
123
563
  });
124
564
  if (provider) {
125
565
  console.log("[ExpoAuthProvider] Provider specified, will skip selection", { provider });
@@ -128,18 +568,13 @@ var ExpoAuthProvider = class {
128
568
  console.log("[ExpoAuthProvider] No provider specified, defaulting to Google");
129
569
  params.append("provider", "google");
130
570
  }
131
- if (customAuthData) {
132
- console.log("[ExpoAuthProvider] Adding custom auth data");
133
- params.append("authData", JSON.stringify(customAuthData));
134
- }
135
571
  const fullAuthUrl = `${baseUrl}?${params.toString()}`;
136
572
  console.log("[ExpoAuthProvider] Starting authentication", {
137
573
  baseUrl,
138
574
  redirectUrl,
139
- organizationId,
575
+ publicKey,
140
576
  sessionId,
141
- provider,
142
- hasCustomData: !!customAuthData
577
+ provider
143
578
  });
144
579
  await WebBrowser.warmUpAsync();
145
580
  const result = await WebBrowser.openAuthSessionAsync(fullAuthUrl, redirectUrl, {
@@ -153,15 +588,32 @@ var ExpoAuthProvider = class {
153
588
  if (result.type === "success" && result.url) {
154
589
  const url = new URL(result.url);
155
590
  const walletId = url.searchParams.get("wallet_id");
156
- const provider2 = url.searchParams.get("provider");
591
+ const organizationId = url.searchParams.get("organization_id");
157
592
  const accountDerivationIndex = url.searchParams.get("selected_account_index");
593
+ const expiresInMs = url.searchParams.get("expires_in_ms");
594
+ const authUserId = url.searchParams.get("auth_user_id");
158
595
  if (!walletId) {
159
596
  throw new Error("Authentication failed: no walletId in redirect URL");
160
597
  }
598
+ if (!organizationId) {
599
+ console.error("[ExpoAuthProvider] Missing organizationId in redirect URL", { url: result.url });
600
+ throw new Error("Authentication failed: no organizationId in redirect URL");
601
+ }
602
+ console.log("[ExpoAuthProvider] Auth redirect parameters", {
603
+ walletId,
604
+ organizationId,
605
+ provider,
606
+ accountDerivationIndex,
607
+ expiresInMs,
608
+ authUserId
609
+ });
161
610
  return {
162
611
  walletId,
163
- provider: provider2 || void 0,
164
- accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : void 0
612
+ organizationId,
613
+ provider: provider || void 0,
614
+ accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
615
+ expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
616
+ authUserId: authUserId || void 0
165
617
  };
166
618
  } else if (result.type === "cancel") {
167
619
  throw new Error("User cancelled authentication");
@@ -181,7 +633,7 @@ var ExpoAuthProvider = class {
181
633
  };
182
634
 
183
635
  // src/providers/embedded/url-params.ts
184
- var import_react_native = require("react-native");
636
+ var import_react_native6 = require("react-native");
185
637
  var ExpoURLParamsAccessor = class {
186
638
  constructor() {
187
639
  this.listeners = /* @__PURE__ */ new Set();
@@ -193,7 +645,7 @@ var ExpoURLParamsAccessor = class {
193
645
  }
194
646
  async getInitialParams() {
195
647
  try {
196
- const url = await import_react_native.Linking.getInitialURL();
648
+ const url = await import_react_native6.Linking.getInitialURL();
197
649
  if (!url) {
198
650
  return null;
199
651
  }
@@ -209,7 +661,7 @@ var ExpoURLParamsAccessor = class {
209
661
  if (this.subscription) {
210
662
  return;
211
663
  }
212
- this.subscription = import_react_native.Linking.addEventListener("url", ({ url }) => {
664
+ this.subscription = import_react_native6.Linking.addEventListener("url", ({ url }) => {
213
665
  const params = this.parseURLParams(url);
214
666
  if (params && Object.keys(params).length > 0) {
215
667
  this.currentParams = { ...this.currentParams, ...params };
@@ -449,17 +901,31 @@ var ExpoLogger = class {
449
901
  }
450
902
  };
451
903
 
904
+ // src/providers/embedded/phantom-app.ts
905
+ var ReactNativePhantomAppProvider = class {
906
+ isAvailable() {
907
+ return false;
908
+ }
909
+ authenticate(_options) {
910
+ return Promise.reject(
911
+ new Error(
912
+ "Phantom app authentication is not available in React Native. Please use other authentication methods like Google, Apple, or JWT."
913
+ )
914
+ );
915
+ }
916
+ };
917
+
452
918
  // src/PhantomProvider.tsx
453
- var import_react_native2 = require("react-native");
454
- var import_jsx_runtime = require("react/jsx-runtime");
455
- var PhantomContext = (0, import_react.createContext)(void 0);
456
- function PhantomProvider({ children, config, debugConfig }) {
457
- const [isConnected, setIsConnected] = (0, import_react.useState)(false);
458
- const [isConnecting, setIsConnecting] = (0, import_react.useState)(false);
459
- const [connectError, setConnectError] = (0, import_react.useState)(null);
460
- const [addresses, setAddresses] = (0, import_react.useState)([]);
461
- const [walletId, setWalletId] = (0, import_react.useState)(null);
462
- const memoizedConfig = (0, import_react.useMemo)(() => {
919
+ var import_react_native7 = require("react-native");
920
+ var import_jsx_runtime6 = require("react/jsx-runtime");
921
+ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appName }) {
922
+ const [isConnected, setIsConnected] = (0, import_react8.useState)(false);
923
+ const [isConnecting, setIsConnecting] = (0, import_react8.useState)(false);
924
+ const [errors, setErrors] = (0, import_react8.useState)({});
925
+ const [addresses, setAddresses] = (0, import_react8.useState)([]);
926
+ const [walletId, setWalletId] = (0, import_react8.useState)(null);
927
+ const [user, setUser] = (0, import_react8.useState)(null);
928
+ const memoizedConfig = (0, import_react8.useMemo)(() => {
463
929
  const redirectUrl = config.authOptions?.redirectUrl || `${config.scheme}://phantom-auth-callback`;
464
930
  return {
465
931
  ...config,
@@ -472,7 +938,7 @@ function PhantomProvider({ children, config, debugConfig }) {
472
938
  }
473
939
  };
474
940
  }, [config]);
475
- const sdk = (0, import_react.useMemo)(() => {
941
+ const sdk = (0, import_react8.useMemo)(() => {
476
942
  const storage = new ExpoSecureStorage();
477
943
  const authProvider = new ExpoAuthProvider();
478
944
  const urlParamsAccessor = new ExpoURLParamsAccessor();
@@ -481,34 +947,36 @@ function PhantomProvider({ children, config, debugConfig }) {
481
947
  keyPrefix: `phantom-rn-${memoizedConfig.appId}`,
482
948
  appId: memoizedConfig.appId
483
949
  });
484
- const platformName = `${import_react_native2.Platform.OS}-${import_react_native2.Platform.Version}`;
950
+ const platformName = `${import_react_native7.Platform.OS}-${import_react_native7.Platform.Version}`;
485
951
  const platform = {
486
952
  storage,
487
953
  authProvider,
488
954
  urlParamsAccessor,
489
955
  stamper,
956
+ phantomAppProvider: new ReactNativePhantomAppProvider(),
490
957
  name: platformName,
491
958
  analyticsHeaders: {
492
959
  [import_constants2.ANALYTICS_HEADERS.SDK_TYPE]: "react-native",
493
- [import_constants2.ANALYTICS_HEADERS.PLATFORM]: import_react_native2.Platform.OS,
494
- [import_constants2.ANALYTICS_HEADERS.PLATFORM_VERSION]: `${import_react_native2.Platform.Version}`,
960
+ [import_constants2.ANALYTICS_HEADERS.PLATFORM]: import_react_native7.Platform.OS,
961
+ [import_constants2.ANALYTICS_HEADERS.PLATFORM_VERSION]: `${import_react_native7.Platform.Version}`,
495
962
  [import_constants2.ANALYTICS_HEADERS.APP_ID]: config.appId,
496
963
  [import_constants2.ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
497
- [import_constants2.ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.9"
964
+ [import_constants2.ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0"
498
965
  // Replaced at build time
499
966
  }
500
967
  };
501
968
  return new import_embedded_provider_core.EmbeddedProvider(memoizedConfig, platform, logger);
502
969
  }, [memoizedConfig, debugConfig, config.appId, config.embeddedWalletType]);
503
- (0, import_react.useEffect)(() => {
970
+ (0, import_react8.useEffect)(() => {
504
971
  const handleConnectStart = () => {
505
972
  setIsConnecting(true);
506
- setConnectError(null);
973
+ setErrors((prev) => ({ ...prev, connect: void 0 }));
507
974
  };
508
- const handleConnect = async () => {
975
+ const handleConnect = async (data) => {
509
976
  try {
510
977
  setIsConnected(true);
511
978
  setIsConnecting(false);
979
+ setUser(data);
512
980
  const addrs = await sdk.getAddresses();
513
981
  setAddresses(addrs);
514
982
  } catch (err) {
@@ -522,110 +990,57 @@ function PhantomProvider({ children, config, debugConfig }) {
522
990
  };
523
991
  const handleConnectError = (errorData) => {
524
992
  setIsConnecting(false);
525
- setConnectError(new Error(errorData.error || "Connection failed"));
993
+ setErrors((prev) => ({ ...prev, connect: new Error(errorData.error || "Connection failed") }));
994
+ setAddresses([]);
526
995
  };
527
996
  const handleDisconnect = () => {
528
997
  setIsConnected(false);
529
998
  setIsConnecting(false);
530
- setConnectError(null);
999
+ setErrors({});
531
1000
  setAddresses([]);
532
1001
  setWalletId(null);
1002
+ setUser(null);
1003
+ };
1004
+ const handleSpendingLimitReached = () => {
1005
+ setErrors((prev) => ({ ...prev, spendingLimit: true }));
533
1006
  };
534
1007
  sdk.on("connect_start", handleConnectStart);
535
1008
  sdk.on("connect", handleConnect);
536
1009
  sdk.on("connect_error", handleConnectError);
537
1010
  sdk.on("disconnect", handleDisconnect);
1011
+ sdk.on("spending_limit_reached", handleSpendingLimitReached);
538
1012
  return () => {
539
1013
  sdk.off("connect_start", handleConnectStart);
540
1014
  sdk.off("connect", handleConnect);
541
1015
  sdk.off("connect_error", handleConnectError);
542
1016
  sdk.off("disconnect", handleDisconnect);
1017
+ sdk.off("spending_limit_reached", handleSpendingLimitReached);
543
1018
  };
544
1019
  }, [sdk]);
545
- (0, import_react.useEffect)(() => {
546
- if (config.autoConnect !== false) {
547
- sdk.autoConnect().catch(() => {
548
- });
549
- }
550
- }, [sdk, config.autoConnect]);
551
- const value = (0, import_react.useMemo)(
1020
+ (0, import_react8.useEffect)(() => {
1021
+ sdk.autoConnect().catch(() => {
1022
+ });
1023
+ }, [sdk]);
1024
+ const clearError = (0, import_react8.useCallback)((key) => {
1025
+ setErrors(({ [key]: _, ...next }) => next);
1026
+ }, []);
1027
+ const value = (0, import_react8.useMemo)(
552
1028
  () => ({
553
1029
  sdk,
554
1030
  isConnected,
555
1031
  isConnecting,
556
- connectError,
1032
+ errors,
557
1033
  addresses,
558
1034
  walletId,
559
- setWalletId
1035
+ setWalletId,
1036
+ user,
1037
+ allowedProviders: config.providers,
1038
+ clearError
560
1039
  }),
561
- [sdk, isConnected, isConnecting, connectError, addresses, walletId, setWalletId]
562
- );
563
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PhantomContext.Provider, { value, children });
564
- }
565
- function usePhantom() {
566
- const context = (0, import_react.useContext)(PhantomContext);
567
- if (context === void 0) {
568
- throw new Error("usePhantom must be used within a PhantomProvider");
569
- }
570
- return context;
571
- }
572
-
573
- // src/hooks/useConnect.ts
574
- var import_react2 = require("react");
575
- function useConnect() {
576
- const { sdk, isConnecting, connectError, setWalletId } = usePhantom();
577
- const connect = (0, import_react2.useCallback)(
578
- async (_options) => {
579
- if (!sdk) {
580
- throw new Error("SDK not initialized");
581
- }
582
- try {
583
- const result = await sdk.connect();
584
- if (result.status === "completed" && result.walletId) {
585
- setWalletId(result.walletId);
586
- }
587
- return result;
588
- } catch (err) {
589
- const error = err;
590
- throw error;
591
- }
592
- },
593
- [sdk, setWalletId]
1040
+ [sdk, isConnected, isConnecting, errors, addresses, walletId, setWalletId, user, config.providers, clearError]
594
1041
  );
595
- return {
596
- connect,
597
- isConnecting,
598
- error: connectError
599
- };
600
- }
601
-
602
- // src/hooks/useDisconnect.ts
603
- var import_react3 = require("react");
604
- function useDisconnect() {
605
- const { sdk } = usePhantom();
606
- const [isDisconnecting, setIsDisconnecting] = (0, import_react3.useState)(false);
607
- const [error, setError] = (0, import_react3.useState)(null);
608
- const disconnect = (0, import_react3.useCallback)(async () => {
609
- if (!sdk) {
610
- throw new Error("SDK not initialized");
611
- }
612
- setIsDisconnecting(true);
613
- setError(null);
614
- try {
615
- await sdk.disconnect();
616
- } catch (err) {
617
- const error2 = err;
618
- setError(error2);
619
- throw error2;
620
- } finally {
621
- setIsDisconnecting(false);
622
- }
623
- }, [sdk]);
624
- return {
625
- disconnect,
626
- isDisconnecting,
627
- error
628
- };
1042
+ const resolvedTheme = theme || import_wallet_sdk_ui5.darkTheme;
1043
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_wallet_sdk_ui5.ThemeProvider, { theme: resolvedTheme, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PhantomContext.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ModalProvider, { appIcon, appName, children }) }) });
629
1044
  }
630
1045
 
631
1046
  // src/hooks/useAccounts.ts
@@ -642,9 +1057,7 @@ function useAccounts() {
642
1057
  function useSolana() {
643
1058
  const { sdk, isConnected } = usePhantom();
644
1059
  return {
645
- // Chain instance with connection enforcement for signing methods
646
1060
  solana: sdk.solana,
647
- // State
648
1061
  isAvailable: !!isConnected
649
1062
  };
650
1063
  }
@@ -663,15 +1076,19 @@ function useEthereum() {
663
1076
  // src/index.ts
664
1077
  var import_client = require("@phantom/client");
665
1078
  var import_constants3 = require("@phantom/constants");
1079
+ var import_wallet_sdk_ui6 = require("@phantom/wallet-sdk-ui");
666
1080
  // Annotate the CommonJS export names for ESM import in node:
667
1081
  0 && (module.exports = {
668
1082
  AddressType,
669
1083
  NetworkId,
670
1084
  PhantomProvider,
1085
+ darkTheme,
1086
+ lightTheme,
671
1087
  useAccounts,
672
1088
  useConnect,
673
1089
  useDisconnect,
674
1090
  useEthereum,
1091
+ useModal,
675
1092
  usePhantom,
676
1093
  useSolana
677
1094
  });