@phantom/react-sdk 1.0.0-beta.1 → 1.0.0-beta.11

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 CHANGED
@@ -52,8 +52,8 @@ function App() {
52
52
 
53
53
  function WalletComponent() {
54
54
  const { connect, isConnecting } = useConnect();
55
- const solana = useSolana();
56
- const ethereum = useEthereum();
55
+ const { solana } = useSolana();
56
+ const { ethereum } = useEthereum();
57
57
 
58
58
  const handleConnect = async () => {
59
59
  const { addresses } = await connect();
@@ -94,9 +94,8 @@ function App() {
94
94
  <PhantomProvider
95
95
  config={{
96
96
  providerType: "embedded",
97
- embeddedWalletType: "app-wallet", // or 'user-wallet'
97
+ appId: "your-app-id", // Get your app ID from phantom.com/portal
98
98
  addressTypes: [AddressType.solana, AddressType.ethereum],
99
- apiBaseUrl: "https://api.phantom.app/v1/wallets",
100
99
  }}
101
100
  >
102
101
  <YourApp />
@@ -116,8 +115,8 @@ The React SDK follows a clear connection pattern:
116
115
  ```tsx
117
116
  function WalletExample() {
118
117
  const { connect } = useConnect();
119
- const solana = useSolana();
120
- const ethereum = useEthereum();
118
+ const { solana } = useSolana();
119
+ const { ethereum } = useEthereum();
121
120
 
122
121
  // 1. Connect first
123
122
  const handleConnect = async () => {
@@ -181,26 +180,7 @@ Uses the Phantom browser extension installed by the user.
181
180
 
182
181
  Creates non-custodial wallets embedded in your application.
183
182
 
184
- #### App Wallet (Recommended for most apps)
185
-
186
- - **New wallets** created per application
187
- - **Unfunded** by default - you need to fund them
188
- - **Independent** from user's existing Phantom wallet
189
-
190
- ```tsx
191
- <PhantomProvider
192
- config={{
193
- providerType: "embedded",
194
- embeddedWalletType: "app-wallet",
195
- addressTypes: [AddressType.solana],
196
- apiBaseUrl: "https://api.phantom.app/v1/wallets",
197
- }}
198
- >
199
- <YourApp />
200
- </PhantomProvider>
201
- ```
202
-
203
- #### User Wallet (For existing Phantom users)
183
+ #### User Wallet
204
184
 
205
185
  - **Uses Phantom authentication** - user logs in with existing account
206
186
  - **Potentially funded** - brings existing wallet balance
@@ -210,41 +190,14 @@ Creates non-custodial wallets embedded in your application.
210
190
  <PhantomProvider
211
191
  config={{
212
192
  providerType: "embedded",
213
- embeddedWalletType: "user-wallet",
193
+ appId: "your-app-id",
214
194
  addressTypes: [AddressType.solana, AddressType.ethereum],
215
- apiBaseUrl: "https://api.phantom.app/v1/wallets",
216
- }}
217
- >
218
- <YourApp />
219
- </PhantomProvider>
220
- ```
221
-
222
- ## Solana Provider Configuration
223
-
224
- When using `AddressType.solana`, you can choose between two Solana libraries:
225
-
226
- ```tsx
227
- <PhantomProvider
228
- config={{
229
- providerType: "embedded",
230
- addressTypes: [AddressType.solana],
231
- solanaProvider: "web3js", // or 'kit'
232
- apiBaseUrl: "https://api.phantom.app/v1/wallets",
233
195
  }}
234
196
  >
235
197
  <YourApp />
236
198
  </PhantomProvider>
237
199
  ```
238
200
 
239
- **Provider Options:**
240
-
241
- - `'web3js'` (default) - Uses `@solana/web3.js` library
242
- - `'kit'` - Uses `@solana/kit` library (modern, TypeScript-first)
243
-
244
- **When to use each:**
245
-
246
- - **@solana/web3.js**: Better ecosystem compatibility, wider community support
247
- - **@solana/kit**: Better TypeScript support, modern architecture, smaller bundle size
248
201
 
249
202
  ## Available Hooks
250
203
 
@@ -363,7 +316,7 @@ import { useSolana } from "@phantom/react-sdk";
363
316
  import { VersionedTransaction, TransactionMessage, SystemProgram, PublicKey, Connection } from "@solana/web3.js";
364
317
 
365
318
  function SolanaOperations() {
366
- const solana = useSolana();
319
+ const { solana } = useSolana();
367
320
 
368
321
  const signMessage = async () => {
369
322
  const signature = await solana.signMessage("Hello Solana!");
@@ -392,7 +345,7 @@ function SolanaOperations() {
392
345
 
393
346
  // Sign and send
394
347
  const result = await solana.signAndSendTransaction(transaction);
395
- console.log("Transaction sent:", result.hash);
348
+ console.log("Transaction sent:", result.signature);
396
349
  };
397
350
 
398
351
  const switchNetwork = async () => {
@@ -428,7 +381,7 @@ Hook for Ethereum chain operations:
428
381
  import { useEthereum } from "@phantom/react-sdk";
429
382
 
430
383
  function EthereumOperations() {
431
- const ethereum = useEthereum();
384
+ const { ethereum } = useEthereum();
432
385
 
433
386
  const signPersonalMessage = async () => {
434
387
  const accounts = await ethereum.getAccounts();
@@ -470,6 +423,16 @@ function EthereumOperations() {
470
423
  console.log("Typed data signature:", signature);
471
424
  };
472
425
 
426
+ const signTransaction = async () => {
427
+ const signedTx = await ethereum.signTransaction({
428
+ to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E",
429
+ value: "1000000000000000000", // 1 ETH in wei
430
+ gas: "21000",
431
+ });
432
+ console.log("Transaction signed:", signedTx);
433
+ // Transaction is signed but not sent - you can broadcast it later
434
+ };
435
+
473
436
  const sendTransaction = async () => {
474
437
  const result = await ethereum.sendTransaction({
475
438
  to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E",
@@ -487,7 +450,8 @@ function EthereumOperations() {
487
450
  <div>
488
451
  <button onClick={signPersonalMessage}>Sign Personal Message</button>
489
452
  <button onClick={signTypedData}>Sign Typed Data</button>
490
- <button onClick={sendTransaction}>Send Transaction</button>
453
+ <button onClick={signTransaction}>Sign Transaction</button>
454
+ <button onClick={sendTransaction}>Sign & Send Transaction</button>
491
455
  <button onClick={switchChain}>Switch to Polygon</button>
492
456
  <p>Connected: {ethereum.isConnected ? "Yes" : "No"}</p>
493
457
  </div>
@@ -500,7 +464,8 @@ function EthereumOperations() {
500
464
  - `request(args)` - EIP-1193 requests
501
465
  - `signPersonalMessage(message, address)` - Sign personal message
502
466
  - `signTypedData(typedData)` - Sign EIP-712 typed data
503
- - `sendTransaction(transaction)` - Send transaction
467
+ - `signTransaction(transaction)` - Sign transaction without sending
468
+ - `sendTransaction(transaction)` - Sign and send transaction
504
469
  - `switchChain(chainId)` - Switch chains
505
470
  - `getChainId()` - Get current chain ID
506
471
  - `getAccounts()` - Get connected accounts
@@ -649,7 +614,7 @@ import { VersionedTransaction, TransactionMessage, SystemProgram, PublicKey, Con
649
614
  import { useSolana } from "@phantom/react-sdk";
650
615
 
651
616
  function SolanaExample() {
652
- const solana = useSolana();
617
+ const { solana } = useSolana();
653
618
 
654
619
  const sendTransaction = async () => {
655
620
  // Get recent blockhash
@@ -675,7 +640,7 @@ function SolanaExample() {
675
640
 
676
641
  // Sign and send using chain-specific hook
677
642
  const result = await solana.signAndSendTransaction(transaction);
678
- console.log("Transaction sent:", result.hash);
643
+ console.log("Transaction sent:", result.signature);
679
644
  };
680
645
 
681
646
  return <button onClick={sendTransaction}>Send SOL</button>;
@@ -697,7 +662,7 @@ import {
697
662
  import { useSolana } from "@phantom/react-sdk";
698
663
 
699
664
  function SolanaKitExample() {
700
- const solana = useSolana();
665
+ const { solana } = useSolana();
701
666
 
702
667
  const sendTransaction = async () => {
703
668
  const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
@@ -714,7 +679,7 @@ function SolanaKitExample() {
714
679
 
715
680
  // Sign and send using chain-specific hook
716
681
  const result = await solana.signAndSendTransaction(transaction);
717
- console.log("Transaction sent:", result.hash);
682
+ console.log("Transaction sent:", result.signature);
718
683
  };
719
684
 
720
685
  return <button onClick={sendTransaction}>Send SOL</button>;
@@ -728,7 +693,7 @@ import { parseEther, parseGwei, encodeFunctionData } from "viem";
728
693
  import { useEthereum } from "@phantom/react-sdk";
729
694
 
730
695
  function EthereumExample() {
731
- const ethereum = useEthereum();
696
+ const { ethereum } = useEthereum();
732
697
 
733
698
  const sendEth = async () => {
734
699
  const result = await ethereum.sendTransaction({
@@ -787,15 +752,16 @@ interface PhantomSDKConfig {
787
752
  addressTypes?: [AddressType, ...AddressType[]]; // Networks to enable (e.g., [AddressType.solana])
788
753
 
789
754
  // Required for embedded provider only
790
- apiBaseUrl?: string; // Phantom API base URL
791
- appId: string; // Your app ID
755
+ appId: string; // Your app ID from phantom.com/portal (required for embedded provider)
756
+
757
+ // Optional configuration
758
+ apiBaseUrl?: string; // Phantom API base URL (optional, has default)
792
759
  authOptions?: {
793
- authUrl?: string; // Custom auth URL (optional)
794
- redirectUrl?: string; // Custom redirect URL (optional)
760
+ authUrl?: string; // Custom auth URL (optional, defaults to "https://connect.phantom.app/login")
761
+ redirectUrl?: string; // Custom redirect URL after authentication (optional)
795
762
  };
796
- embeddedWalletType?: "app-wallet" | "user-wallet"; // Wallet type
797
- solanaProvider?: "web3js" | "kit"; // Solana library choice (default: 'web3js')
798
- autoConnect?: boolean; // Auto-connect to existing session on SDK instantiation (default: true for embedded, false for injected)
763
+ embeddedWalletType?: "user-wallet"; // Wallet type (optional, defaults to "user-wallet", currently the only supported type)
764
+ autoConnect?: boolean; // Auto-connect to existing session on SDK instantiation (optional, defaults to true for embedded, false for injected)
799
765
  }
800
766
  ```
801
767
 
@@ -827,6 +793,7 @@ function App() {
827
793
  // SDK configuration - static, won't change when debug settings change
828
794
  const config: PhantomSDKConfig = {
829
795
  providerType: "embedded",
796
+ appId: "your-app-id",
830
797
  // ... other config
831
798
  };
832
799
 
package/dist/index.d.ts CHANGED
@@ -3,11 +3,10 @@ import { ReactNode } from 'react';
3
3
  import { BrowserSDKConfig, DebugConfig, AuthOptions, BrowserSDK, WalletAddress, AutoConfirmEnableParams, AutoConfirmResult, AutoConfirmSupportedChainsResult } from '@phantom/browser-sdk';
4
4
  export { AddressType, AutoConfirmEnableParams, AutoConfirmResult, AutoConfirmSupportedChainsResult, DebugLevel, DebugMessage, NetworkId, SignedTransaction, WalletAddress, debug } from '@phantom/browser-sdk';
5
5
  import * as _phantom_embedded_provider_core from '@phantom/embedded-provider-core';
6
- import { ISolanaChain, IEthereumChain, EthTransactionRequest } from '@phantom/chains';
7
- export { EthTransactionRequest, IEthereumChain, ISolanaChain } from '@phantom/chains';
6
+ import { ISolanaChain, IEthereumChain } from '@phantom/chain-interfaces';
7
+ export { EthTransactionRequest, IEthereumChain, ISolanaChain } from '@phantom/chain-interfaces';
8
8
 
9
- interface PhantomSDKConfig extends BrowserSDKConfig {
10
- }
9
+ type PhantomSDKConfig = BrowserSDKConfig;
11
10
  interface PhantomDebugConfig extends DebugConfig {
12
11
  }
13
12
  interface ConnectOptions {
@@ -24,6 +23,7 @@ interface PhantomContextValue {
24
23
  walletId: string | null;
25
24
  currentProviderType: "injected" | "embedded" | null;
26
25
  isPhantomAvailable: boolean;
26
+ isClient: boolean;
27
27
  }
28
28
  interface PhantomProviderProps {
29
29
  children: ReactNode;
@@ -72,50 +72,21 @@ declare function useAutoConfirm(): UseAutoConfirmResult;
72
72
  /**
73
73
  * Hook for Solana chain operations
74
74
  *
75
- * @returns Solana chain interface and convenient methods
75
+ * @returns Solana chain interface with connection enforcement
76
76
  */
77
77
  declare function useSolana(): {
78
- solana: ISolanaChain | null;
79
- signMessage: (message: string | Uint8Array) => Promise<{
80
- signature: Uint8Array;
81
- publicKey: string;
82
- }>;
83
- signTransaction: <T>(transaction: T) => Promise<T>;
84
- signAndSendTransaction: <T>(transaction: T) => Promise<{
85
- signature: string;
86
- }>;
87
- connect: (options?: {
88
- onlyIfTrusted?: boolean;
89
- }) => Promise<{
90
- publicKey: string;
91
- }>;
92
- disconnect: () => Promise<void>;
93
- switchNetwork: (network: "mainnet" | "devnet") => Promise<void | undefined>;
94
- getPublicKey: () => Promise<string | null>;
78
+ solana: ISolanaChain;
95
79
  isAvailable: boolean;
96
- isConnected: boolean;
97
80
  };
98
81
 
99
82
  /**
100
83
  * Hook for Ethereum chain operations
101
84
  *
102
- * @returns Ethereum chain interface and convenient methods
85
+ * @returns Ethereum chain interface with connection enforcement
103
86
  */
104
87
  declare function useEthereum(): {
105
- ethereum: IEthereumChain | null;
106
- request: <T = any>(args: {
107
- method: string;
108
- params?: unknown[];
109
- }) => Promise<T>;
110
- signPersonalMessage: (message: string, address: string) => Promise<string>;
111
- signMessage: (message: string) => Promise<string>;
112
- signTypedData: (typedData: any) => Promise<string>;
113
- sendTransaction: (transaction: EthTransactionRequest) => Promise<string>;
114
- switchChain: (chainId: number) => Promise<void>;
115
- getChainId: () => Promise<number>;
116
- getAccounts: () => Promise<string[]>;
88
+ ethereum: IEthereumChain;
117
89
  isAvailable: boolean;
118
- isConnected: boolean;
119
90
  };
120
91
 
121
92
  type ProviderType = "injected" | "embedded";
package/dist/index.js CHANGED
@@ -52,13 +52,9 @@ var import_browser_sdk = require("@phantom/browser-sdk");
52
52
  var import_jsx_runtime = require("react/jsx-runtime");
53
53
  var PhantomContext = (0, import_react.createContext)(void 0);
54
54
  function PhantomProvider({ children, config, debugConfig }) {
55
- const memoizedConfig = (0, import_react.useMemo)(() => {
56
- return {
57
- ...config,
58
- // Use providerType if provided, default to embedded
59
- providerType: config.providerType || "embedded"
60
- };
61
- }, [config]);
55
+ const memoizedConfig = (0, import_react.useMemo)(() => config, [config]);
56
+ const [sdk, setSdk] = (0, import_react.useState)(null);
57
+ const [isClient, setIsClient] = (0, import_react.useState)(false);
62
58
  const [isConnected, setIsConnected] = (0, import_react.useState)(false);
63
59
  const [isConnecting, setIsConnecting] = (0, import_react.useState)(false);
64
60
  const [connectError, setConnectError] = (0, import_react.useState)(null);
@@ -68,9 +64,18 @@ function PhantomProvider({ children, config, debugConfig }) {
68
64
  memoizedConfig.providerType || null
69
65
  );
70
66
  const [isPhantomAvailable, setIsPhantomAvailable] = (0, import_react.useState)(false);
71
- const [sdk, setSdk] = (0, import_react.useState)(null);
72
67
  (0, import_react.useEffect)(() => {
68
+ setIsClient(true);
69
+ }, []);
70
+ (0, import_react.useEffect)(() => {
71
+ if (!isClient)
72
+ return;
73
73
  const sdkInstance = new import_browser_sdk.BrowserSDK(memoizedConfig);
74
+ setSdk(sdkInstance);
75
+ }, [isClient, memoizedConfig]);
76
+ (0, import_react.useEffect)(() => {
77
+ if (!sdk)
78
+ return;
74
79
  const handleConnectStart = () => {
75
80
  setIsConnecting(true);
76
81
  setConnectError(null);
@@ -79,15 +84,15 @@ function PhantomProvider({ children, config, debugConfig }) {
79
84
  try {
80
85
  setIsConnected(true);
81
86
  setIsConnecting(false);
82
- const providerInfo = sdkInstance.getCurrentProviderInfo();
87
+ const providerInfo = sdk.getCurrentProviderInfo();
83
88
  setCurrentProviderType(providerInfo?.type || null);
84
- const addrs = await sdkInstance.getAddresses();
89
+ const addrs = await sdk.getAddresses();
85
90
  setAddresses(addrs);
86
- setWalletId(sdkInstance.getWalletId());
91
+ setWalletId(sdk.getWalletId());
87
92
  } catch (err) {
88
93
  console.error("Error connecting:", err);
89
94
  try {
90
- await sdkInstance.disconnect();
95
+ await sdk.disconnect();
91
96
  } catch (err2) {
92
97
  console.error("Error disconnecting:", err2);
93
98
  }
@@ -105,25 +110,24 @@ function PhantomProvider({ children, config, debugConfig }) {
105
110
  setAddresses([]);
106
111
  setWalletId(null);
107
112
  };
108
- sdkInstance.on("connect_start", handleConnectStart);
109
- sdkInstance.on("connect", handleConnect);
110
- sdkInstance.on("connect_error", handleConnectError);
111
- sdkInstance.on("disconnect", handleDisconnect);
112
- setSdk(sdkInstance);
113
+ sdk.on("connect_start", handleConnectStart);
114
+ sdk.on("connect", handleConnect);
115
+ sdk.on("connect_error", handleConnectError);
116
+ sdk.on("disconnect", handleDisconnect);
113
117
  return () => {
114
- sdkInstance.off("connect_start", handleConnectStart);
115
- sdkInstance.off("connect", handleConnect);
116
- sdkInstance.off("connect_error", handleConnectError);
117
- sdkInstance.off("disconnect", handleDisconnect);
118
+ sdk.off("connect_start", handleConnectStart);
119
+ sdk.off("connect", handleConnect);
120
+ sdk.off("connect_error", handleConnectError);
121
+ sdk.off("disconnect", handleDisconnect);
118
122
  };
119
- }, [memoizedConfig]);
123
+ }, [sdk]);
120
124
  (0, import_react.useEffect)(() => {
121
- if (!sdk || !debugConfig)
125
+ if (!debugConfig || !sdk)
122
126
  return;
123
127
  sdk.configureDebug(debugConfig);
124
128
  }, [sdk, debugConfig]);
125
129
  (0, import_react.useEffect)(() => {
126
- if (!sdk)
130
+ if (!isClient || !sdk)
127
131
  return;
128
132
  const initialize = async () => {
129
133
  try {
@@ -139,7 +143,7 @@ function PhantomProvider({ children, config, debugConfig }) {
139
143
  }
140
144
  };
141
145
  initialize();
142
- }, [sdk, memoizedConfig.autoConnect]);
146
+ }, [sdk, memoizedConfig.autoConnect, isClient]);
143
147
  const value = (0, import_react.useMemo)(
144
148
  () => ({
145
149
  sdk,
@@ -149,9 +153,10 @@ function PhantomProvider({ children, config, debugConfig }) {
149
153
  addresses,
150
154
  walletId,
151
155
  currentProviderType,
152
- isPhantomAvailable
156
+ isPhantomAvailable,
157
+ isClient
153
158
  }),
154
- [sdk, isConnected, isConnecting, connectError, addresses, walletId, currentProviderType, isPhantomAvailable]
159
+ [sdk, isConnected, isConnecting, connectError, addresses, walletId, currentProviderType, isPhantomAvailable, isClient]
155
160
  );
156
161
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PhantomContext.Provider, { value, children });
157
162
  }
@@ -353,176 +358,38 @@ function useAutoConfirm() {
353
358
  }
354
359
 
355
360
  // src/hooks/useSolana.ts
356
- var import_react5 = require("react");
357
361
  function useSolana() {
358
- const { sdk, isConnected } = usePhantom();
359
- const solanaChain = (0, import_react5.useMemo)(() => {
360
- if (!sdk || !isConnected)
361
- return null;
362
- try {
363
- return sdk.solana;
364
- } catch {
365
- return null;
366
- }
367
- }, [sdk, isConnected]);
368
- const signMessage = (0, import_react5.useCallback)(
369
- async (message) => {
370
- if (!solanaChain)
371
- throw new Error("Solana chain not available. Ensure SDK is connected.");
372
- return solanaChain.signMessage(message);
373
- },
374
- [solanaChain]
375
- );
376
- const signTransaction = (0, import_react5.useCallback)(
377
- async (transaction) => {
378
- if (!solanaChain)
379
- throw new Error("Solana chain not available. Ensure SDK is connected.");
380
- return solanaChain.signTransaction(transaction);
381
- },
382
- [solanaChain]
383
- );
384
- const signAndSendTransaction = (0, import_react5.useCallback)(
385
- async (transaction) => {
386
- if (!solanaChain)
387
- throw new Error("Solana chain not available. Ensure SDK is connected.");
388
- return solanaChain.signAndSendTransaction(transaction);
389
- },
390
- [solanaChain]
391
- );
392
- const connect = (0, import_react5.useCallback)(
393
- async (options) => {
394
- if (!solanaChain)
395
- throw new Error("Solana chain not available. Ensure SDK is connected.");
396
- return solanaChain.connect(options);
397
- },
398
- [solanaChain]
399
- );
400
- const disconnect = (0, import_react5.useCallback)(async () => {
401
- if (!solanaChain)
402
- throw new Error("Solana chain not available. Ensure SDK is connected.");
403
- return solanaChain.disconnect();
404
- }, [solanaChain]);
405
- const switchNetwork = (0, import_react5.useCallback)(
406
- async (network) => {
407
- if (!solanaChain)
408
- throw new Error("Solana chain not available. Ensure SDK is connected.");
409
- return solanaChain.switchNetwork?.(network);
410
- },
411
- [solanaChain]
412
- );
413
- const getPublicKey = (0, import_react5.useCallback)(async () => {
414
- if (!solanaChain)
415
- return null;
416
- return solanaChain.getPublicKey();
417
- }, [solanaChain]);
362
+ const { sdk, isConnected, isClient } = usePhantom();
363
+ if (!isClient || !sdk) {
364
+ return {
365
+ solana: {},
366
+ // This will be replaced when SDK is ready
367
+ isAvailable: false
368
+ };
369
+ }
418
370
  return {
419
- // Chain instance for advanced usage
420
- solana: solanaChain,
421
- // Convenient methods
422
- signMessage,
423
- signTransaction,
424
- signAndSendTransaction,
425
- connect,
426
- disconnect,
427
- switchNetwork,
428
- getPublicKey,
371
+ // Chain instance with connection enforcement for signing methods
372
+ solana: sdk.solana,
429
373
  // State
430
- isAvailable: !!solanaChain,
431
- isConnected: solanaChain?.isConnected() ?? false
374
+ isAvailable: !!isConnected
432
375
  };
433
376
  }
434
377
 
435
378
  // src/hooks/useEthereum.ts
436
- var import_react6 = require("react");
437
379
  function useEthereum() {
438
- const { sdk, isConnected } = usePhantom();
439
- const ethereumChain = (0, import_react6.useMemo)(() => {
440
- if (!sdk || !isConnected)
441
- return null;
442
- try {
443
- return sdk.ethereum;
444
- } catch {
445
- return null;
446
- }
447
- }, [sdk, isConnected]);
448
- const request = (0, import_react6.useCallback)(
449
- async (args) => {
450
- if (!ethereumChain)
451
- throw new Error("Ethereum chain not available. Ensure SDK is connected.");
452
- return ethereumChain.request(args);
453
- },
454
- [ethereumChain]
455
- );
456
- const signPersonalMessage = (0, import_react6.useCallback)(
457
- async (message, address) => {
458
- return request({
459
- method: "personal_sign",
460
- params: [message, address]
461
- });
462
- },
463
- [request]
464
- );
465
- const sendTransaction = (0, import_react6.useCallback)(
466
- async (transaction) => {
467
- if (!ethereumChain)
468
- throw new Error("Ethereum chain not available. Ensure SDK is connected.");
469
- return ethereumChain.sendTransaction(transaction);
470
- },
471
- [ethereumChain]
472
- );
473
- const switchChain = (0, import_react6.useCallback)(
474
- async (chainId) => {
475
- if (!ethereumChain)
476
- throw new Error("Ethereum chain not available. Ensure SDK is connected.");
477
- return ethereumChain.switchChain(chainId);
478
- },
479
- [ethereumChain]
480
- );
481
- const getChainId = (0, import_react6.useCallback)(async () => {
482
- if (!ethereumChain)
483
- throw new Error("Ethereum chain not available. Ensure SDK is connected.");
484
- return ethereumChain.getChainId();
485
- }, [ethereumChain]);
486
- const getAccounts = (0, import_react6.useCallback)(async () => {
487
- if (!ethereumChain)
488
- throw new Error("Ethereum chain not available. Ensure SDK is connected.");
489
- return ethereumChain.getAccounts();
490
- }, [ethereumChain]);
491
- const signMessage = (0, import_react6.useCallback)(
492
- async (message) => {
493
- return request({
494
- method: "eth_sign",
495
- params: [await getAccounts().then((accounts) => accounts[0]), message]
496
- });
497
- },
498
- [request, getAccounts]
499
- );
500
- const signTypedData = (0, import_react6.useCallback)(
501
- async (typedData) => {
502
- const accounts = await getAccounts();
503
- return request({
504
- method: "eth_signTypedData_v4",
505
- params: [accounts[0], JSON.stringify(typedData)]
506
- });
507
- },
508
- [request, getAccounts]
509
- );
380
+ const { sdk, isConnected, isClient } = usePhantom();
381
+ if (!isClient || !sdk) {
382
+ return {
383
+ ethereum: {},
384
+ // This will be replaced when SDK is ready
385
+ isAvailable: false
386
+ };
387
+ }
510
388
  return {
511
- // Chain instance for advanced usage
512
- ethereum: ethereumChain,
513
- // Standard EIP-1193 interface
514
- request,
515
- // Convenient methods
516
- signPersonalMessage,
517
- signMessage,
518
- signTypedData,
519
- sendTransaction,
520
- switchChain,
521
- getChainId,
522
- getAccounts,
389
+ // Chain instance with connection enforcement for signing methods
390
+ ethereum: sdk.ethereum,
523
391
  // State
524
- isAvailable: !!ethereumChain,
525
- isConnected: ethereumChain?.isConnected() ?? false
392
+ isAvailable: !!isConnected
526
393
  };
527
394
  }
528
395
 
package/dist/index.mjs CHANGED
@@ -4,13 +4,9 @@ import { BrowserSDK } from "@phantom/browser-sdk";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  var PhantomContext = createContext(void 0);
6
6
  function PhantomProvider({ children, config, debugConfig }) {
7
- const memoizedConfig = useMemo(() => {
8
- return {
9
- ...config,
10
- // Use providerType if provided, default to embedded
11
- providerType: config.providerType || "embedded"
12
- };
13
- }, [config]);
7
+ const memoizedConfig = useMemo(() => config, [config]);
8
+ const [sdk, setSdk] = useState(null);
9
+ const [isClient, setIsClient] = useState(false);
14
10
  const [isConnected, setIsConnected] = useState(false);
15
11
  const [isConnecting, setIsConnecting] = useState(false);
16
12
  const [connectError, setConnectError] = useState(null);
@@ -20,9 +16,18 @@ function PhantomProvider({ children, config, debugConfig }) {
20
16
  memoizedConfig.providerType || null
21
17
  );
22
18
  const [isPhantomAvailable, setIsPhantomAvailable] = useState(false);
23
- const [sdk, setSdk] = useState(null);
24
19
  useEffect(() => {
20
+ setIsClient(true);
21
+ }, []);
22
+ useEffect(() => {
23
+ if (!isClient)
24
+ return;
25
25
  const sdkInstance = new BrowserSDK(memoizedConfig);
26
+ setSdk(sdkInstance);
27
+ }, [isClient, memoizedConfig]);
28
+ useEffect(() => {
29
+ if (!sdk)
30
+ return;
26
31
  const handleConnectStart = () => {
27
32
  setIsConnecting(true);
28
33
  setConnectError(null);
@@ -31,15 +36,15 @@ function PhantomProvider({ children, config, debugConfig }) {
31
36
  try {
32
37
  setIsConnected(true);
33
38
  setIsConnecting(false);
34
- const providerInfo = sdkInstance.getCurrentProviderInfo();
39
+ const providerInfo = sdk.getCurrentProviderInfo();
35
40
  setCurrentProviderType(providerInfo?.type || null);
36
- const addrs = await sdkInstance.getAddresses();
41
+ const addrs = await sdk.getAddresses();
37
42
  setAddresses(addrs);
38
- setWalletId(sdkInstance.getWalletId());
43
+ setWalletId(sdk.getWalletId());
39
44
  } catch (err) {
40
45
  console.error("Error connecting:", err);
41
46
  try {
42
- await sdkInstance.disconnect();
47
+ await sdk.disconnect();
43
48
  } catch (err2) {
44
49
  console.error("Error disconnecting:", err2);
45
50
  }
@@ -57,25 +62,24 @@ function PhantomProvider({ children, config, debugConfig }) {
57
62
  setAddresses([]);
58
63
  setWalletId(null);
59
64
  };
60
- sdkInstance.on("connect_start", handleConnectStart);
61
- sdkInstance.on("connect", handleConnect);
62
- sdkInstance.on("connect_error", handleConnectError);
63
- sdkInstance.on("disconnect", handleDisconnect);
64
- setSdk(sdkInstance);
65
+ sdk.on("connect_start", handleConnectStart);
66
+ sdk.on("connect", handleConnect);
67
+ sdk.on("connect_error", handleConnectError);
68
+ sdk.on("disconnect", handleDisconnect);
65
69
  return () => {
66
- sdkInstance.off("connect_start", handleConnectStart);
67
- sdkInstance.off("connect", handleConnect);
68
- sdkInstance.off("connect_error", handleConnectError);
69
- sdkInstance.off("disconnect", handleDisconnect);
70
+ sdk.off("connect_start", handleConnectStart);
71
+ sdk.off("connect", handleConnect);
72
+ sdk.off("connect_error", handleConnectError);
73
+ sdk.off("disconnect", handleDisconnect);
70
74
  };
71
- }, [memoizedConfig]);
75
+ }, [sdk]);
72
76
  useEffect(() => {
73
- if (!sdk || !debugConfig)
77
+ if (!debugConfig || !sdk)
74
78
  return;
75
79
  sdk.configureDebug(debugConfig);
76
80
  }, [sdk, debugConfig]);
77
81
  useEffect(() => {
78
- if (!sdk)
82
+ if (!isClient || !sdk)
79
83
  return;
80
84
  const initialize = async () => {
81
85
  try {
@@ -91,7 +95,7 @@ function PhantomProvider({ children, config, debugConfig }) {
91
95
  }
92
96
  };
93
97
  initialize();
94
- }, [sdk, memoizedConfig.autoConnect]);
98
+ }, [sdk, memoizedConfig.autoConnect, isClient]);
95
99
  const value = useMemo(
96
100
  () => ({
97
101
  sdk,
@@ -101,9 +105,10 @@ function PhantomProvider({ children, config, debugConfig }) {
101
105
  addresses,
102
106
  walletId,
103
107
  currentProviderType,
104
- isPhantomAvailable
108
+ isPhantomAvailable,
109
+ isClient
105
110
  }),
106
- [sdk, isConnected, isConnecting, connectError, addresses, walletId, currentProviderType, isPhantomAvailable]
111
+ [sdk, isConnected, isConnecting, connectError, addresses, walletId, currentProviderType, isPhantomAvailable, isClient]
107
112
  );
108
113
  return /* @__PURE__ */ jsx(PhantomContext.Provider, { value, children });
109
114
  }
@@ -305,176 +310,38 @@ function useAutoConfirm() {
305
310
  }
306
311
 
307
312
  // src/hooks/useSolana.ts
308
- import { useCallback as useCallback4, useMemo as useMemo2 } from "react";
309
313
  function useSolana() {
310
- const { sdk, isConnected } = usePhantom();
311
- const solanaChain = useMemo2(() => {
312
- if (!sdk || !isConnected)
313
- return null;
314
- try {
315
- return sdk.solana;
316
- } catch {
317
- return null;
318
- }
319
- }, [sdk, isConnected]);
320
- const signMessage = useCallback4(
321
- async (message) => {
322
- if (!solanaChain)
323
- throw new Error("Solana chain not available. Ensure SDK is connected.");
324
- return solanaChain.signMessage(message);
325
- },
326
- [solanaChain]
327
- );
328
- const signTransaction = useCallback4(
329
- async (transaction) => {
330
- if (!solanaChain)
331
- throw new Error("Solana chain not available. Ensure SDK is connected.");
332
- return solanaChain.signTransaction(transaction);
333
- },
334
- [solanaChain]
335
- );
336
- const signAndSendTransaction = useCallback4(
337
- async (transaction) => {
338
- if (!solanaChain)
339
- throw new Error("Solana chain not available. Ensure SDK is connected.");
340
- return solanaChain.signAndSendTransaction(transaction);
341
- },
342
- [solanaChain]
343
- );
344
- const connect = useCallback4(
345
- async (options) => {
346
- if (!solanaChain)
347
- throw new Error("Solana chain not available. Ensure SDK is connected.");
348
- return solanaChain.connect(options);
349
- },
350
- [solanaChain]
351
- );
352
- const disconnect = useCallback4(async () => {
353
- if (!solanaChain)
354
- throw new Error("Solana chain not available. Ensure SDK is connected.");
355
- return solanaChain.disconnect();
356
- }, [solanaChain]);
357
- const switchNetwork = useCallback4(
358
- async (network) => {
359
- if (!solanaChain)
360
- throw new Error("Solana chain not available. Ensure SDK is connected.");
361
- return solanaChain.switchNetwork?.(network);
362
- },
363
- [solanaChain]
364
- );
365
- const getPublicKey = useCallback4(async () => {
366
- if (!solanaChain)
367
- return null;
368
- return solanaChain.getPublicKey();
369
- }, [solanaChain]);
314
+ const { sdk, isConnected, isClient } = usePhantom();
315
+ if (!isClient || !sdk) {
316
+ return {
317
+ solana: {},
318
+ // This will be replaced when SDK is ready
319
+ isAvailable: false
320
+ };
321
+ }
370
322
  return {
371
- // Chain instance for advanced usage
372
- solana: solanaChain,
373
- // Convenient methods
374
- signMessage,
375
- signTransaction,
376
- signAndSendTransaction,
377
- connect,
378
- disconnect,
379
- switchNetwork,
380
- getPublicKey,
323
+ // Chain instance with connection enforcement for signing methods
324
+ solana: sdk.solana,
381
325
  // State
382
- isAvailable: !!solanaChain,
383
- isConnected: solanaChain?.isConnected() ?? false
326
+ isAvailable: !!isConnected
384
327
  };
385
328
  }
386
329
 
387
330
  // src/hooks/useEthereum.ts
388
- import { useCallback as useCallback5, useMemo as useMemo3 } from "react";
389
331
  function useEthereum() {
390
- const { sdk, isConnected } = usePhantom();
391
- const ethereumChain = useMemo3(() => {
392
- if (!sdk || !isConnected)
393
- return null;
394
- try {
395
- return sdk.ethereum;
396
- } catch {
397
- return null;
398
- }
399
- }, [sdk, isConnected]);
400
- const request = useCallback5(
401
- async (args) => {
402
- if (!ethereumChain)
403
- throw new Error("Ethereum chain not available. Ensure SDK is connected.");
404
- return ethereumChain.request(args);
405
- },
406
- [ethereumChain]
407
- );
408
- const signPersonalMessage = useCallback5(
409
- async (message, address) => {
410
- return request({
411
- method: "personal_sign",
412
- params: [message, address]
413
- });
414
- },
415
- [request]
416
- );
417
- const sendTransaction = useCallback5(
418
- async (transaction) => {
419
- if (!ethereumChain)
420
- throw new Error("Ethereum chain not available. Ensure SDK is connected.");
421
- return ethereumChain.sendTransaction(transaction);
422
- },
423
- [ethereumChain]
424
- );
425
- const switchChain = useCallback5(
426
- async (chainId) => {
427
- if (!ethereumChain)
428
- throw new Error("Ethereum chain not available. Ensure SDK is connected.");
429
- return ethereumChain.switchChain(chainId);
430
- },
431
- [ethereumChain]
432
- );
433
- const getChainId = useCallback5(async () => {
434
- if (!ethereumChain)
435
- throw new Error("Ethereum chain not available. Ensure SDK is connected.");
436
- return ethereumChain.getChainId();
437
- }, [ethereumChain]);
438
- const getAccounts = useCallback5(async () => {
439
- if (!ethereumChain)
440
- throw new Error("Ethereum chain not available. Ensure SDK is connected.");
441
- return ethereumChain.getAccounts();
442
- }, [ethereumChain]);
443
- const signMessage = useCallback5(
444
- async (message) => {
445
- return request({
446
- method: "eth_sign",
447
- params: [await getAccounts().then((accounts) => accounts[0]), message]
448
- });
449
- },
450
- [request, getAccounts]
451
- );
452
- const signTypedData = useCallback5(
453
- async (typedData) => {
454
- const accounts = await getAccounts();
455
- return request({
456
- method: "eth_signTypedData_v4",
457
- params: [accounts[0], JSON.stringify(typedData)]
458
- });
459
- },
460
- [request, getAccounts]
461
- );
332
+ const { sdk, isConnected, isClient } = usePhantom();
333
+ if (!isClient || !sdk) {
334
+ return {
335
+ ethereum: {},
336
+ // This will be replaced when SDK is ready
337
+ isAvailable: false
338
+ };
339
+ }
462
340
  return {
463
- // Chain instance for advanced usage
464
- ethereum: ethereumChain,
465
- // Standard EIP-1193 interface
466
- request,
467
- // Convenient methods
468
- signPersonalMessage,
469
- signMessage,
470
- signTypedData,
471
- sendTransaction,
472
- switchChain,
473
- getChainId,
474
- getAccounts,
341
+ // Chain instance with connection enforcement for signing methods
342
+ ethereum: sdk.ethereum,
475
343
  // State
476
- isAvailable: !!ethereumChain,
477
- isConnected: ethereumChain?.isConnected() ?? false
344
+ isAvailable: !!isConnected
478
345
  };
479
346
  }
480
347
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phantom/react-sdk",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.11",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -26,9 +26,9 @@
26
26
  "prettier": "prettier --write \"src/**/*.{ts,tsx}\""
27
27
  },
28
28
  "dependencies": {
29
- "@phantom/browser-sdk": "^1.0.0-beta.1",
30
- "@phantom/chains": "^1.0.0-beta.1",
31
- "@phantom/constants": "^1.0.0-beta.1"
29
+ "@phantom/browser-sdk": "^1.0.0-beta.11",
30
+ "@phantom/chain-interfaces": "^1.0.0-beta.6",
31
+ "@phantom/constants": "^1.0.0-beta.6"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@testing-library/dom": "^10.4.0",