@phantom/react-native-sdk 1.0.0-beta.2 → 1.0.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,12 +1,14 @@
1
1
  // src/PhantomProvider.tsx
2
2
  import { createContext, useContext, useState, useEffect, useMemo } from "react";
3
3
  import { EmbeddedProvider } from "@phantom/embedded-provider-core";
4
+ import { ANALYTICS_HEADERS, DEFAULT_WALLET_API_URL, DEFAULT_EMBEDDED_WALLET_TYPE, DEFAULT_AUTH_URL as DEFAULT_AUTH_URL2 } from "@phantom/constants";
4
5
 
5
6
  // src/providers/embedded/storage.ts
6
7
  import * as SecureStore from "expo-secure-store";
7
8
  var ExpoSecureStorage = class {
8
9
  constructor(requireAuth = false) {
9
10
  this.sessionKey = "phantom_session";
11
+ this.logoutFlagKey = "phantom_should_clear_previous_session";
10
12
  this.requireAuth = requireAuth;
11
13
  }
12
14
  async saveSession(session) {
@@ -41,6 +43,36 @@ var ExpoSecureStorage = class {
41
43
  console.error("[ExpoSecureStorage] Failed to clear session", { error: error.message });
42
44
  }
43
45
  }
46
+ async getShouldClearPreviousSession() {
47
+ try {
48
+ const flagData = await SecureStore.getItemAsync(this.logoutFlagKey, {
49
+ requireAuthentication: false
50
+ // Don't require auth for this flag
51
+ });
52
+ if (!flagData) {
53
+ return false;
54
+ }
55
+ return flagData === "true";
56
+ } catch (error) {
57
+ console.error("[ExpoSecureStorage] Failed to get shouldClearPreviousSession flag", {
58
+ error: error.message
59
+ });
60
+ return false;
61
+ }
62
+ }
63
+ async setShouldClearPreviousSession(should) {
64
+ try {
65
+ await SecureStore.setItemAsync(this.logoutFlagKey, should.toString(), {
66
+ requireAuthentication: false,
67
+ // Don't require auth for this flag
68
+ keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY
69
+ });
70
+ } catch (error) {
71
+ console.error("[ExpoSecureStorage] Failed to set shouldClearPreviousSession flag", {
72
+ error: error.message
73
+ });
74
+ }
75
+ }
44
76
  async isAvailable() {
45
77
  return await SecureStore.isAvailableAsync();
46
78
  }
@@ -52,29 +84,34 @@ var ExpoSecureStorage = class {
52
84
 
53
85
  // src/providers/embedded/auth.ts
54
86
  import * as WebBrowser from "expo-web-browser";
55
- var DEFAULT_AUTH_URL = "https://auth.phantom.app";
87
+ import { Platform } from "react-native";
88
+ import { DEFAULT_AUTH_URL } from "@phantom/constants";
56
89
  var ExpoAuthProvider = class {
57
90
  async authenticate(options) {
58
91
  if ("jwtToken" in options) {
59
92
  return;
60
93
  }
61
94
  const phantomOptions = options;
62
- const { authUrl, redirectUrl, organizationId, parentOrganizationId, sessionId, provider, customAuthData, appId } = phantomOptions;
95
+ const { authUrl, redirectUrl, publicKey, sessionId, provider, appId } = phantomOptions;
63
96
  if (!redirectUrl) {
64
97
  throw new Error("redirectUrl is required for web browser authentication");
65
98
  }
66
- if (!organizationId || !sessionId || !appId) {
67
- throw new Error("organizationId, sessionId and appId are required for authentication");
99
+ if (!publicKey || !sessionId || !appId) {
100
+ throw new Error("publicKey, sessionId and appId are required for authentication");
68
101
  }
69
102
  try {
70
103
  const baseUrl = authUrl || DEFAULT_AUTH_URL;
71
104
  const params = new URLSearchParams({
72
- organization_id: organizationId,
73
- parent_organization_id: parentOrganizationId,
105
+ public_key: publicKey,
74
106
  app_id: appId,
75
107
  redirect_uri: redirectUrl,
76
108
  session_id: sessionId,
77
- clear_previous_session: "true"
109
+ // OAuth session management - defaults to allow refresh unless explicitly clearing after logout
110
+ clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
111
+ allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
112
+ sdk_version: "1.0.0-beta.21",
113
+ sdk_type: "react-native",
114
+ platform: Platform.OS
78
115
  });
79
116
  if (provider) {
80
117
  console.log("[ExpoAuthProvider] Provider specified, will skip selection", { provider });
@@ -83,19 +120,13 @@ var ExpoAuthProvider = class {
83
120
  console.log("[ExpoAuthProvider] No provider specified, defaulting to Google");
84
121
  params.append("provider", "google");
85
122
  }
86
- if (customAuthData) {
87
- console.log("[ExpoAuthProvider] Adding custom auth data");
88
- params.append("authData", JSON.stringify(customAuthData));
89
- }
90
123
  const fullAuthUrl = `${baseUrl}?${params.toString()}`;
91
124
  console.log("[ExpoAuthProvider] Starting authentication", {
92
125
  baseUrl,
93
126
  redirectUrl,
94
- organizationId,
95
- parentOrganizationId,
127
+ publicKey,
96
128
  sessionId,
97
- provider,
98
- hasCustomData: !!customAuthData
129
+ provider
99
130
  });
100
131
  await WebBrowser.warmUpAsync();
101
132
  const result = await WebBrowser.openAuthSessionAsync(fullAuthUrl, redirectUrl, {
@@ -109,15 +140,33 @@ var ExpoAuthProvider = class {
109
140
  if (result.type === "success" && result.url) {
110
141
  const url = new URL(result.url);
111
142
  const walletId = url.searchParams.get("wallet_id");
143
+ const organizationId = url.searchParams.get("organization_id");
112
144
  const provider2 = url.searchParams.get("provider");
113
145
  const accountDerivationIndex = url.searchParams.get("selected_account_index");
146
+ const expiresInMs = url.searchParams.get("expires_in_ms");
147
+ const authUserId = url.searchParams.get("auth_user_id");
114
148
  if (!walletId) {
115
149
  throw new Error("Authentication failed: no walletId in redirect URL");
116
150
  }
151
+ if (!organizationId) {
152
+ console.error("[ExpoAuthProvider] Missing organizationId in redirect URL", { url: result.url });
153
+ throw new Error("Authentication failed: no organizationId in redirect URL");
154
+ }
155
+ console.log("[ExpoAuthProvider] Auth redirect parameters", {
156
+ walletId,
157
+ organizationId,
158
+ provider: provider2,
159
+ accountDerivationIndex,
160
+ expiresInMs,
161
+ authUserId
162
+ });
117
163
  return {
118
164
  walletId,
165
+ organizationId,
119
166
  provider: provider2 || void 0,
120
- accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : void 0
167
+ accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
168
+ expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
169
+ authUserId: authUserId || void 0
121
170
  };
122
171
  } else if (result.type === "cancel") {
123
172
  throw new Error("User cancelled authentication");
@@ -218,7 +267,7 @@ var ReactNativeStamper = class {
218
267
  this.algorithm = Algorithm.ed25519;
219
268
  this.type = "PKI";
220
269
  this.keyPrefix = config.keyPrefix || "phantom-rn-stamper";
221
- this.organizationId = config.organizationId || "default";
270
+ this.appId = config.appId || "default";
222
271
  }
223
272
  /**
224
273
  * Initialize the stamper and generate/load cryptographic keys
@@ -371,10 +420,10 @@ var ReactNativeStamper = class {
371
420
  return null;
372
421
  }
373
422
  getActiveKeyName() {
374
- return `${this.keyPrefix}-${this.organizationId}-active`;
423
+ return `${this.keyPrefix}-${this.appId}-active`;
375
424
  }
376
425
  getPendingKeyName() {
377
- return `${this.keyPrefix}-${this.organizationId}-pending`;
426
+ return `${this.keyPrefix}-${this.appId}-pending`;
378
427
  }
379
428
  };
380
429
 
@@ -405,8 +454,22 @@ var ExpoLogger = class {
405
454
  }
406
455
  };
407
456
 
457
+ // src/providers/embedded/phantom-app.ts
458
+ var ReactNativePhantomAppProvider = class {
459
+ isAvailable() {
460
+ return false;
461
+ }
462
+ authenticate(_options) {
463
+ return Promise.reject(
464
+ new Error(
465
+ "Phantom app authentication is not available in React Native. Please use other authentication methods like Google, Apple, or JWT."
466
+ )
467
+ );
468
+ }
469
+ };
470
+
408
471
  // src/PhantomProvider.tsx
409
- import { Platform } from "react-native";
472
+ import { Platform as Platform2 } from "react-native";
410
473
  import { jsx } from "react/jsx-runtime";
411
474
  var PhantomContext = createContext(void 0);
412
475
  function PhantomProvider({ children, config, debugConfig }) {
@@ -415,48 +478,65 @@ function PhantomProvider({ children, config, debugConfig }) {
415
478
  const [connectError, setConnectError] = useState(null);
416
479
  const [addresses, setAddresses] = useState([]);
417
480
  const [walletId, setWalletId] = useState(null);
418
- const [sdk, setSdk] = useState(null);
481
+ const [user, setUser] = useState(null);
419
482
  const memoizedConfig = useMemo(() => {
420
483
  const redirectUrl = config.authOptions?.redirectUrl || `${config.scheme}://phantom-auth-callback`;
421
484
  return {
422
485
  ...config,
486
+ apiBaseUrl: config.apiBaseUrl || DEFAULT_WALLET_API_URL,
487
+ embeddedWalletType: config.embeddedWalletType || DEFAULT_EMBEDDED_WALLET_TYPE,
423
488
  authOptions: {
424
489
  ...config.authOptions || {},
425
- redirectUrl
490
+ redirectUrl,
491
+ authUrl: config.authOptions?.authUrl || DEFAULT_AUTH_URL2
426
492
  }
427
493
  };
428
494
  }, [config]);
429
- useEffect(() => {
495
+ const sdk = useMemo(() => {
430
496
  const storage = new ExpoSecureStorage();
431
497
  const authProvider = new ExpoAuthProvider();
432
498
  const urlParamsAccessor = new ExpoURLParamsAccessor();
433
499
  const logger = new ExpoLogger(debugConfig?.enabled || false);
434
500
  const stamper = new ReactNativeStamper({
435
- keyPrefix: `phantom-rn-${memoizedConfig.organizationId}`,
436
- organizationId: memoizedConfig.organizationId
501
+ keyPrefix: `phantom-rn-${memoizedConfig.appId}`,
502
+ appId: memoizedConfig.appId
437
503
  });
504
+ const platformName = `${Platform2.OS}-${Platform2.Version}`;
438
505
  const platform = {
439
506
  storage,
440
507
  authProvider,
441
508
  urlParamsAccessor,
442
509
  stamper,
443
- name: `${Platform.OS}-${Platform.Version}`
510
+ phantomAppProvider: new ReactNativePhantomAppProvider(),
511
+ name: platformName,
512
+ analyticsHeaders: {
513
+ [ANALYTICS_HEADERS.SDK_TYPE]: "react-native",
514
+ [ANALYTICS_HEADERS.PLATFORM]: Platform2.OS,
515
+ [ANALYTICS_HEADERS.PLATFORM_VERSION]: `${Platform2.Version}`,
516
+ [ANALYTICS_HEADERS.APP_ID]: config.appId,
517
+ [ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
518
+ [ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.21"
519
+ // Replaced at build time
520
+ }
444
521
  };
445
- const sdkInstance = new EmbeddedProvider(memoizedConfig, platform, logger);
522
+ return new EmbeddedProvider(memoizedConfig, platform, logger);
523
+ }, [memoizedConfig, debugConfig, config.appId, config.embeddedWalletType]);
524
+ useEffect(() => {
446
525
  const handleConnectStart = () => {
447
526
  setIsConnecting(true);
448
527
  setConnectError(null);
449
528
  };
450
- const handleConnect = async () => {
529
+ const handleConnect = async (data) => {
451
530
  try {
452
531
  setIsConnected(true);
453
532
  setIsConnecting(false);
454
- const addrs = await sdkInstance.getAddresses();
533
+ setUser(data);
534
+ const addrs = await sdk.getAddresses();
455
535
  setAddresses(addrs);
456
536
  } catch (err) {
457
537
  console.error("Error connecting:", err);
458
538
  try {
459
- await sdkInstance.disconnect();
539
+ await sdk.disconnect();
460
540
  } catch (err2) {
461
541
  console.error("Error disconnecting:", err2);
462
542
  }
@@ -465,6 +545,7 @@ function PhantomProvider({ children, config, debugConfig }) {
465
545
  const handleConnectError = (errorData) => {
466
546
  setIsConnecting(false);
467
547
  setConnectError(new Error(errorData.error || "Connection failed"));
548
+ setAddresses([]);
468
549
  };
469
550
  const handleDisconnect = () => {
470
551
  setIsConnected(false);
@@ -472,22 +553,20 @@ function PhantomProvider({ children, config, debugConfig }) {
472
553
  setConnectError(null);
473
554
  setAddresses([]);
474
555
  setWalletId(null);
556
+ setUser(null);
475
557
  };
476
- sdkInstance.on("connect_start", handleConnectStart);
477
- sdkInstance.on("connect", handleConnect);
478
- sdkInstance.on("connect_error", handleConnectError);
479
- sdkInstance.on("disconnect", handleDisconnect);
480
- setSdk(sdkInstance);
558
+ sdk.on("connect_start", handleConnectStart);
559
+ sdk.on("connect", handleConnect);
560
+ sdk.on("connect_error", handleConnectError);
561
+ sdk.on("disconnect", handleDisconnect);
481
562
  return () => {
482
- sdkInstance.off("connect_start", handleConnectStart);
483
- sdkInstance.off("connect", handleConnect);
484
- sdkInstance.off("connect_error", handleConnectError);
485
- sdkInstance.off("disconnect", handleDisconnect);
563
+ sdk.off("connect_start", handleConnectStart);
564
+ sdk.off("connect", handleConnect);
565
+ sdk.off("connect_error", handleConnectError);
566
+ sdk.off("disconnect", handleDisconnect);
486
567
  };
487
- }, [memoizedConfig, debugConfig]);
568
+ }, [sdk]);
488
569
  useEffect(() => {
489
- if (!sdk)
490
- return;
491
570
  if (config.autoConnect !== false) {
492
571
  sdk.autoConnect().catch(() => {
493
572
  });
@@ -501,9 +580,10 @@ function PhantomProvider({ children, config, debugConfig }) {
501
580
  connectError,
502
581
  addresses,
503
582
  walletId,
504
- setWalletId
583
+ setWalletId,
584
+ user
505
585
  }),
506
- [sdk, isConnected, isConnecting, connectError, addresses, walletId, setWalletId]
586
+ [sdk, isConnected, isConnecting, connectError, addresses, walletId, setWalletId, user]
507
587
  );
508
588
  return /* @__PURE__ */ jsx(PhantomContext.Provider, { value, children });
509
589
  }
@@ -520,12 +600,12 @@ import { useCallback } from "react";
520
600
  function useConnect() {
521
601
  const { sdk, isConnecting, connectError, setWalletId } = usePhantom();
522
602
  const connect = useCallback(
523
- async (_options) => {
603
+ async (options) => {
524
604
  if (!sdk) {
525
605
  throw new Error("SDK not initialized");
526
606
  }
527
607
  try {
528
- const result = await sdk.connect();
608
+ const result = await sdk.connect(options);
529
609
  if (result.status === "completed" && result.walletId) {
530
610
  setWalletId(result.walletId);
531
611
  }
@@ -584,184 +664,24 @@ function useAccounts() {
584
664
  }
585
665
 
586
666
  // src/hooks/useSolana.ts
587
- import { useCallback as useCallback3, useMemo as useMemo2 } from "react";
588
667
  function useSolana() {
589
668
  const { sdk, isConnected } = usePhantom();
590
- const getSolanaChain = useCallback3(() => {
591
- if (!sdk)
592
- throw new Error("Phantom SDK not initialized.");
593
- if (!sdk.isConnected())
594
- throw new Error("Phantom SDK not connected. Call connect() first.");
595
- return sdk.solana;
596
- }, [sdk]);
597
- const solanaChain = useMemo2(() => {
598
- if (!sdk || !isConnected)
599
- return null;
600
- try {
601
- return sdk.solana;
602
- } catch {
603
- return null;
604
- }
605
- }, [sdk, isConnected]);
606
- const signMessage = useCallback3(
607
- async (message) => {
608
- const chain = getSolanaChain();
609
- return await chain.signMessage(message);
610
- },
611
- [getSolanaChain]
612
- );
613
- const signTransaction = useCallback3(
614
- async (transaction) => {
615
- const chain = getSolanaChain();
616
- return await chain.signTransaction(transaction);
617
- },
618
- [getSolanaChain]
619
- );
620
- const signAndSendTransaction = useCallback3(
621
- async (transaction) => {
622
- const chain = getSolanaChain();
623
- return await chain.signAndSendTransaction(transaction);
624
- },
625
- [getSolanaChain]
626
- );
627
- const connect = useCallback3(
628
- async (options) => {
629
- const chain = getSolanaChain();
630
- return await chain.connect(options);
631
- },
632
- [getSolanaChain]
633
- );
634
- const disconnect = useCallback3(async () => {
635
- const chain = getSolanaChain();
636
- return await chain.disconnect();
637
- }, [getSolanaChain]);
638
- const switchNetwork = useCallback3(
639
- async (network) => {
640
- const chain = getSolanaChain();
641
- return await chain.switchNetwork?.(network);
642
- },
643
- [getSolanaChain]
644
- );
645
- const getPublicKey = useCallback3(async () => {
646
- if (!sdk || !sdk.isConnected())
647
- return null;
648
- return await sdk.solana.getPublicKey();
649
- }, [sdk]);
650
669
  return {
651
- // Chain instance for advanced usage
652
- solana: solanaChain,
653
- // Convenient methods
654
- signMessage,
655
- signTransaction,
656
- signAndSendTransaction,
657
- connect,
658
- disconnect,
659
- switchNetwork,
660
- getPublicKey,
670
+ // Chain instance with connection enforcement for signing methods
671
+ solana: sdk.solana,
661
672
  // State
662
- isAvailable: !!solanaChain,
663
- isConnected: solanaChain?.isConnected() ?? false
673
+ isAvailable: !!isConnected
664
674
  };
665
675
  }
666
676
 
667
677
  // src/hooks/useEthereum.ts
668
- import { useCallback as useCallback4, useMemo as useMemo3 } from "react";
669
678
  function useEthereum() {
670
679
  const { sdk, isConnected } = usePhantom();
671
- const getEthereumChain = useCallback4(() => {
672
- if (!sdk)
673
- throw new Error("Phantom SDK not initialized.");
674
- if (!sdk.isConnected())
675
- throw new Error("Phantom SDK not connected. Call connect() first.");
676
- return sdk.ethereum;
677
- }, [sdk]);
678
- const ethereumChain = useMemo3(() => {
679
- if (!sdk || !isConnected)
680
- return null;
681
- try {
682
- return sdk.ethereum;
683
- } catch {
684
- return null;
685
- }
686
- }, [sdk, isConnected]);
687
- const request = useCallback4(
688
- async (args) => {
689
- const chain = getEthereumChain();
690
- return await chain.request(args);
691
- },
692
- [getEthereumChain]
693
- );
694
- const signPersonalMessage = useCallback4(
695
- async (message, address) => {
696
- const chain = getEthereumChain();
697
- return await chain.signPersonalMessage(message, address);
698
- },
699
- [getEthereumChain]
700
- );
701
- const signTransaction = useCallback4(
702
- async (transaction) => {
703
- const chain = getEthereumChain();
704
- return await chain.signTransaction(transaction);
705
- },
706
- [getEthereumChain]
707
- );
708
- const sendTransaction = useCallback4(
709
- async (transaction) => {
710
- const chain = getEthereumChain();
711
- return await chain.sendTransaction(transaction);
712
- },
713
- [getEthereumChain]
714
- );
715
- const switchChain = useCallback4(
716
- async (chainId) => {
717
- const chain = getEthereumChain();
718
- return await chain.switchChain(chainId);
719
- },
720
- [getEthereumChain]
721
- );
722
- const getChainId = useCallback4(async () => {
723
- const chain = getEthereumChain();
724
- return await chain.getChainId();
725
- }, [getEthereumChain]);
726
- const getAccounts = useCallback4(async () => {
727
- const chain = getEthereumChain();
728
- return await chain.getAccounts();
729
- }, [getEthereumChain]);
730
- const signMessage = useCallback4(
731
- async (message) => {
732
- const accounts = await getAccounts();
733
- return await request({
734
- method: "eth_sign",
735
- params: [accounts[0], message]
736
- });
737
- },
738
- [request, getAccounts]
739
- );
740
- const signTypedData = useCallback4(
741
- async (typedData) => {
742
- const chain = getEthereumChain();
743
- const accounts = await getAccounts();
744
- return await chain.signTypedData(typedData, accounts[0]);
745
- },
746
- [getEthereumChain, getAccounts]
747
- );
748
680
  return {
749
- // Chain instance for advanced usage
750
- ethereum: ethereumChain,
751
- // Standard EIP-1193 interface
752
- request,
753
- // Convenient methods
754
- signPersonalMessage,
755
- signMessage,
756
- signTransaction,
757
- signTypedData,
758
- sendTransaction,
759
- switchChain,
760
- getChainId,
761
- getAccounts,
681
+ // Chain instance with connection enforcement for signing methods
682
+ ethereum: sdk.ethereum,
762
683
  // State
763
- isAvailable: !!ethereumChain,
764
- isConnected: ethereumChain?.isConnected() ?? false
684
+ isAvailable: !!isConnected
765
685
  };
766
686
  }
767
687
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phantom/react-native-sdk",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.21",
4
4
  "description": "Phantom Wallet SDK for React Native and Expo applications",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -45,14 +45,14 @@
45
45
  "directory": "packages/react-native-sdk"
46
46
  },
47
47
  "dependencies": {
48
- "@phantom/api-key-stamper": "^1.0.0-beta.2",
49
- "@phantom/base64url": "^1.0.0-beta.2",
50
- "@phantom/chains": "^1.0.0-beta.2",
51
- "@phantom/client": "^1.0.0-beta.2",
52
- "@phantom/constants": "^1.0.0-beta.2",
53
- "@phantom/crypto": "^1.0.0-beta.2",
54
- "@phantom/embedded-provider-core": "^1.0.0-beta.2",
55
- "@phantom/sdk-types": "^1.0.0-beta.2",
48
+ "@phantom/api-key-stamper": "^1.0.0-beta.9",
49
+ "@phantom/base64url": "^1.0.0-beta.9",
50
+ "@phantom/chain-interfaces": "^1.0.0-beta.9",
51
+ "@phantom/client": "^1.0.0-beta.21",
52
+ "@phantom/constants": "^1.0.0-beta.9",
53
+ "@phantom/crypto": "^1.0.0-beta.9",
54
+ "@phantom/embedded-provider-core": "^1.0.0-beta.21",
55
+ "@phantom/sdk-types": "^1.0.0-beta.9",
56
56
  "@types/bs58": "^5.0.0",
57
57
  "bs58": "^6.0.0",
58
58
  "buffer": "^6.0.3"