@phantom/react-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/README.md +609 -58
- package/dist/index.d.ts +75 -22
- package/dist/index.js +969 -175
- package/dist/index.mjs +975 -172
- package/package.json +13 -6
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ function App() {
|
|
|
41
41
|
return (
|
|
42
42
|
<PhantomProvider
|
|
43
43
|
config={{
|
|
44
|
-
|
|
44
|
+
providers: ["injected"], // Only allow Phantom browser extension
|
|
45
45
|
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
46
46
|
}}
|
|
47
47
|
>
|
|
@@ -52,11 +52,11 @@ 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
|
-
const { addresses } = await connect();
|
|
59
|
+
const { addresses } = await connect({ provider: "injected" });
|
|
60
60
|
console.log("Connected addresses:", addresses);
|
|
61
61
|
};
|
|
62
62
|
|
|
@@ -83,7 +83,7 @@ function WalletComponent() {
|
|
|
83
83
|
}
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
###
|
|
86
|
+
### Multiple Authentication Methods
|
|
87
87
|
|
|
88
88
|
```tsx
|
|
89
89
|
import { PhantomProvider } from "@phantom/react-sdk";
|
|
@@ -93,8 +93,8 @@ function App() {
|
|
|
93
93
|
return (
|
|
94
94
|
<PhantomProvider
|
|
95
95
|
config={{
|
|
96
|
-
|
|
97
|
-
appId: "your-app-id", // Get your app ID from phantom.com/portal
|
|
96
|
+
providers: ["google", "apple", "phantom", "injected"], // Allow all auth methods
|
|
97
|
+
appId: "your-app-id", // Get your app ID from phantom.com/portal (required for embedded providers)
|
|
98
98
|
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
99
99
|
}}
|
|
100
100
|
>
|
|
@@ -104,6 +104,354 @@ function App() {
|
|
|
104
104
|
}
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
+
## Connection Modal
|
|
108
|
+
|
|
109
|
+
The SDK includes a built-in connection modal UI that provides a user-friendly interface for connecting to Phantom. The modal supports multiple connection methods (Google, Apple, Phantom Login, browser extension) and handles all connection logic automatically.
|
|
110
|
+
|
|
111
|
+
### Using the Modal
|
|
112
|
+
|
|
113
|
+
To use the modal, pass a `theme` prop to `PhantomProvider` and use the `useModal()` hook to control visibility:
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { PhantomProvider, useModal, darkTheme, usePhantom } from "@phantom/react-sdk";
|
|
117
|
+
import { AddressType } from "@phantom/browser-sdk";
|
|
118
|
+
|
|
119
|
+
function App() {
|
|
120
|
+
return (
|
|
121
|
+
<PhantomProvider
|
|
122
|
+
config={{
|
|
123
|
+
providers: ["google", "apple", "phantom", "injected"],
|
|
124
|
+
appId: "your-app-id",
|
|
125
|
+
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
126
|
+
}}
|
|
127
|
+
theme={darkTheme}
|
|
128
|
+
appIcon="https://your-app.com/icon.png"
|
|
129
|
+
appName="Your App Name"
|
|
130
|
+
>
|
|
131
|
+
<WalletComponent />
|
|
132
|
+
</PhantomProvider>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function WalletComponent() {
|
|
137
|
+
const { open, close, isOpened } = useModal();
|
|
138
|
+
const { isConnected, user } = usePhantom();
|
|
139
|
+
|
|
140
|
+
if (isConnected) {
|
|
141
|
+
return (
|
|
142
|
+
<div>
|
|
143
|
+
<p>Connected as {user?.email || "Unknown"}</p>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return <button onClick={open}>Connect Wallet</button>;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Modal Features:**
|
|
153
|
+
|
|
154
|
+
- **Multiple Auth Providers**: Google, Apple, Phantom Login, browser extension
|
|
155
|
+
- **Automatic Provider Detection**: Shows browser extension option when Phantom is installed
|
|
156
|
+
- **Mobile Support**: Displays deeplink option for Phantom mobile app on mobile devices
|
|
157
|
+
- **Error Handling**: Clear error messages displayed in the modal
|
|
158
|
+
- **Loading States**: Visual feedback during connection attempts
|
|
159
|
+
- **Responsive Design**: Optimized for both mobile and desktop
|
|
160
|
+
|
|
161
|
+
### useModal Hook
|
|
162
|
+
|
|
163
|
+
Control the connection modal visibility:
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
import { useModal } from "@phantom/react-sdk";
|
|
167
|
+
|
|
168
|
+
function ConnectButton() {
|
|
169
|
+
const { open, close, isOpened } = useModal();
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div>
|
|
173
|
+
<button onClick={open}>Open Modal</button>
|
|
174
|
+
<button onClick={close}>Close Modal</button>
|
|
175
|
+
<p>Modal is {isOpened ? "open" : "closed"}</p>
|
|
176
|
+
</div>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Returns:**
|
|
182
|
+
|
|
183
|
+
- `open()` - Function to open the modal
|
|
184
|
+
- `close()` - Function to close the modal
|
|
185
|
+
- `isOpened` - Boolean indicating if modal is currently visible
|
|
186
|
+
|
|
187
|
+
### ConnectButton Component
|
|
188
|
+
|
|
189
|
+
A ready-to-use button component that handles the complete connection flow. When disconnected, it shows a "Connect Wallet" button that opens the connection modal. When connected, it displays the truncated wallet address and opens the wallet management modal on click.
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
import { ConnectButton, AddressType } from "@phantom/react-sdk";
|
|
193
|
+
|
|
194
|
+
function Header() {
|
|
195
|
+
return (
|
|
196
|
+
<div>
|
|
197
|
+
{/* Default: Shows first available address */}
|
|
198
|
+
<ConnectButton />
|
|
199
|
+
|
|
200
|
+
{/* Show specific address type */}
|
|
201
|
+
<ConnectButton addressType={AddressType.solana} />
|
|
202
|
+
<ConnectButton addressType={AddressType.ethereum} />
|
|
203
|
+
|
|
204
|
+
{/* Full width button */}
|
|
205
|
+
<ConnectButton fullWidth />
|
|
206
|
+
</div>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Props:**
|
|
212
|
+
|
|
213
|
+
- `addressType?: AddressType` - Specify which address type to display when connected (e.g., `AddressType.solana`, `AddressType.ethereum`). If not specified, shows the first available address.
|
|
214
|
+
- `fullWidth?: boolean` - Whether the button should take full width of its container. Default: `false`
|
|
215
|
+
|
|
216
|
+
**Features:**
|
|
217
|
+
|
|
218
|
+
- **When disconnected**: Opens connection modal with auth provider options (Google, Apple, Phantom Login, browser extension)
|
|
219
|
+
- **When connected**: Displays truncated address (e.g., "5Gv8r2...k3Hn") and opens wallet management modal on click
|
|
220
|
+
- **Wallet modal**: Shows all connected addresses and provides a disconnect button
|
|
221
|
+
- Uses theme styling for consistent appearance
|
|
222
|
+
- Fully clickable in both states
|
|
223
|
+
|
|
224
|
+
### ConnectBox Component
|
|
225
|
+
|
|
226
|
+
An inline embedded component that displays the connection UI directly in your page layout (without a modal backdrop). Perfect for auth callback pages or when you want a more integrated connection experience. The component automatically handles all connection states including loading, error, and success during the auth callback flow.
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
import { ConnectBox } from "@phantom/react-sdk";
|
|
230
|
+
|
|
231
|
+
function AuthCallbackPage() {
|
|
232
|
+
return (
|
|
233
|
+
<div>
|
|
234
|
+
<h1>Connecting to Phantom...</h1>
|
|
235
|
+
<ConnectBox />
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Props:**
|
|
242
|
+
|
|
243
|
+
- `maxWidth?: string | number` - Maximum width of the box. Can be a string (e.g., `"500px"`) or number (e.g., `500`). Default: `"350px"`
|
|
244
|
+
- `transparent?: boolean` - When `true`, removes background, border, and shadow for a transparent appearance. Default: `false`
|
|
245
|
+
- `appIcon?: string` - URL to your app icon (optional, can also be set via `PhantomProvider`)
|
|
246
|
+
- `appName?: string` - Your app name (optional, can also be set via `PhantomProvider`)
|
|
247
|
+
|
|
248
|
+
**Usage Examples:**
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { ConnectBox } from "@phantom/react-sdk";
|
|
252
|
+
|
|
253
|
+
// Default usage
|
|
254
|
+
<ConnectBox />
|
|
255
|
+
|
|
256
|
+
// Custom width
|
|
257
|
+
<ConnectBox maxWidth="500px" />
|
|
258
|
+
|
|
259
|
+
// Transparent (no background/border)
|
|
260
|
+
<ConnectBox transparent />
|
|
261
|
+
|
|
262
|
+
// Custom width with transparent
|
|
263
|
+
<ConnectBox maxWidth={600} transparent />
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Features:**
|
|
267
|
+
|
|
268
|
+
- **Inline embedded**: Renders directly in page flow (not as a floating modal)
|
|
269
|
+
- **Auto state management**: Automatically shows connection/login UI when disconnected, wallet info when connected
|
|
270
|
+
- **Auth callback support**: Handles loading and error states during OAuth callback flows
|
|
271
|
+
- **No close button**: Designed for embedded use cases where users shouldn't dismiss the UI
|
|
272
|
+
- **Theme-aware**: Uses your configured theme for consistent styling
|
|
273
|
+
- **Responsive**: Adapts to mobile and desktop layouts
|
|
274
|
+
|
|
275
|
+
**Use Cases:**
|
|
276
|
+
|
|
277
|
+
- Auth callback pages (e.g., `/auth/callback`) where users return from OAuth providers
|
|
278
|
+
- Embedded wallet connection flows in your app's layout
|
|
279
|
+
- Custom connection pages where you want full control over the page design
|
|
280
|
+
|
|
281
|
+
### Handling Auth Callback Pages
|
|
282
|
+
|
|
283
|
+
When using embedded authentication providers (Google, Apple, Phantom Login, etc.), users are redirected to your app's callback URL after authentication. The SDK automatically handles the callback and completes the connection. Here's how to build a callback page if you're not using `ConnectBox`:
|
|
284
|
+
|
|
285
|
+
**Basic Auth Callback Page:**
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
import { usePhantom, useConnect, useAccounts } from "@phantom/react-sdk";
|
|
289
|
+
import { useNavigate } from "react-router-dom"; // or your router
|
|
290
|
+
|
|
291
|
+
function AuthCallbackPage() {
|
|
292
|
+
const navigate = useNavigate();
|
|
293
|
+
const { isConnected } = usePhantom();
|
|
294
|
+
const { isConnecting, error: connectError } = useConnect();
|
|
295
|
+
const addresses = useAccounts();
|
|
296
|
+
|
|
297
|
+
const handleGoHome = () => {
|
|
298
|
+
navigate("/");
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// Loading state - SDK is processing the callback
|
|
302
|
+
if (isConnecting) {
|
|
303
|
+
return (
|
|
304
|
+
<div>
|
|
305
|
+
<h1>Connecting to wallet...</h1>
|
|
306
|
+
<p>Please wait while we complete your authentication.</p>
|
|
307
|
+
</div>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Success state - connection completed
|
|
312
|
+
if (isConnected && addresses && addresses.length > 0) {
|
|
313
|
+
return (
|
|
314
|
+
<div>
|
|
315
|
+
<h1>Authentication Successful</h1>
|
|
316
|
+
<p>You are now connected to your wallet.</p>
|
|
317
|
+
<div>
|
|
318
|
+
<h2>Addresses:</h2>
|
|
319
|
+
{addresses.map((addr, index) => (
|
|
320
|
+
<div key={index}>
|
|
321
|
+
<strong>{addr.addressType}:</strong> {addr.address}
|
|
322
|
+
</div>
|
|
323
|
+
))}
|
|
324
|
+
</div>
|
|
325
|
+
<button onClick={handleGoHome}>Go to Main App</button>
|
|
326
|
+
</div>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Error state - connection failed
|
|
331
|
+
if (connectError) {
|
|
332
|
+
return (
|
|
333
|
+
<div>
|
|
334
|
+
<h1>Authentication Failed</h1>
|
|
335
|
+
<p>{connectError.message || "An unknown error occurred during authentication."}</p>
|
|
336
|
+
<div>
|
|
337
|
+
<button onClick={handleGoHome}>Go to Main App</button>
|
|
338
|
+
</div>
|
|
339
|
+
</div>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Default state (shouldn't normally reach here)
|
|
344
|
+
return (
|
|
345
|
+
<div>
|
|
346
|
+
<h1>Processing authentication...</h1>
|
|
347
|
+
</div>
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Key Points:**
|
|
353
|
+
|
|
354
|
+
- The SDK's `autoConnect()` automatically processes the callback URL parameters when the page loads
|
|
355
|
+
- Use `useConnect()` to access `isConnecting` and `error` states during the callback flow
|
|
356
|
+
- Use `usePhantom()` to check `isConnected` status
|
|
357
|
+
- Use `useAccounts()` to get connected wallet addresses
|
|
358
|
+
- The connection state will automatically update as the SDK processes the callback
|
|
359
|
+
- You can monitor `connectError` to handle authentication failures
|
|
360
|
+
|
|
361
|
+
**Router Setup:**
|
|
362
|
+
|
|
363
|
+
Make sure your callback route is configured in your router:
|
|
364
|
+
|
|
365
|
+
```tsx
|
|
366
|
+
import { Routes, Route } from "react-router-dom";
|
|
367
|
+
import { PhantomProvider } from "@phantom/react-sdk";
|
|
368
|
+
|
|
369
|
+
function App() {
|
|
370
|
+
return (
|
|
371
|
+
<PhantomProvider config={config} theme={darkTheme}>
|
|
372
|
+
<Routes>
|
|
373
|
+
<Route path="/auth/callback" element={<AuthCallbackPage />} />
|
|
374
|
+
<Route path="/" element={<MainApp />} />
|
|
375
|
+
</Routes>
|
|
376
|
+
</PhantomProvider>
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Note:** For a simpler implementation, consider using the `ConnectBox` component which handles all these states automatically.
|
|
382
|
+
|
|
383
|
+
## Theming
|
|
384
|
+
|
|
385
|
+
Customize the modal appearance by passing a theme object to the `PhantomProvider`. The SDK includes two built-in themes: `darkTheme` (default) and `lightTheme`.
|
|
386
|
+
|
|
387
|
+
### Using Built-in Themes
|
|
388
|
+
|
|
389
|
+
```tsx
|
|
390
|
+
import { PhantomProvider, darkTheme, lightTheme } from "@phantom/react-sdk";
|
|
391
|
+
|
|
392
|
+
// Use dark theme (default)
|
|
393
|
+
<PhantomProvider config={config} theme={darkTheme}>
|
|
394
|
+
<App />
|
|
395
|
+
</PhantomProvider>
|
|
396
|
+
|
|
397
|
+
// Use light theme
|
|
398
|
+
<PhantomProvider config={config} theme={lightTheme}>
|
|
399
|
+
<App />
|
|
400
|
+
</PhantomProvider>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Custom Theme
|
|
404
|
+
|
|
405
|
+
You can pass a partial theme object to customize specific properties:
|
|
406
|
+
|
|
407
|
+
```tsx
|
|
408
|
+
import { PhantomProvider } from "@phantom/react-sdk";
|
|
409
|
+
|
|
410
|
+
const customTheme = {
|
|
411
|
+
background: "#1a1a1a",
|
|
412
|
+
text: "#ffffff",
|
|
413
|
+
secondary: "#98979C",
|
|
414
|
+
brand: "#ab9ff2",
|
|
415
|
+
error: "#ff4444",
|
|
416
|
+
success: "#00ff00",
|
|
417
|
+
borderRadius: "16px",
|
|
418
|
+
overlay: "rgba(0, 0, 0, 0.8)",
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
<PhantomProvider config={config} theme={customTheme} appIcon="https://your-app.com/icon.png" appName="Your App">
|
|
422
|
+
<App />
|
|
423
|
+
</PhantomProvider>;
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Theme Properties
|
|
427
|
+
|
|
428
|
+
| Property | Type | Description |
|
|
429
|
+
| -------------- | -------- | --------------------------------------------------------- |
|
|
430
|
+
| `background` | `string` | Background color for modal |
|
|
431
|
+
| `text` | `string` | Primary text color |
|
|
432
|
+
| `secondary` | `string` | Secondary color for text, borders, dividers (must be hex) |
|
|
433
|
+
| `brand` | `string` | Brand/primary action color |
|
|
434
|
+
| `error` | `string` | Error state color |
|
|
435
|
+
| `success` | `string` | Success state color |
|
|
436
|
+
| `borderRadius` | `string` | Border radius for buttons and modal |
|
|
437
|
+
| `overlay` | `string` | Overlay background color (with opacity) |
|
|
438
|
+
|
|
439
|
+
**Note:** The `secondary` color must be a hex color value (e.g., `#98979C`) as it's used to derive auxiliary colors with opacity.
|
|
440
|
+
|
|
441
|
+
### Accessing Theme in Custom Components
|
|
442
|
+
|
|
443
|
+
If you need to access the current theme in your own components, use the `useTheme()` hook:
|
|
444
|
+
|
|
445
|
+
```tsx
|
|
446
|
+
import { useTheme } from "@phantom/react-sdk";
|
|
447
|
+
|
|
448
|
+
function CustomComponent() {
|
|
449
|
+
const theme = useTheme();
|
|
450
|
+
|
|
451
|
+
return <div style={{ backgroundColor: theme.background, color: theme.text }}>Themed content</div>;
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
107
455
|
## Connection Flow
|
|
108
456
|
|
|
109
457
|
The React SDK follows a clear connection pattern:
|
|
@@ -115,12 +463,12 @@ The React SDK follows a clear connection pattern:
|
|
|
115
463
|
```tsx
|
|
116
464
|
function WalletExample() {
|
|
117
465
|
const { connect } = useConnect();
|
|
118
|
-
const solana = useSolana();
|
|
119
|
-
const ethereum = useEthereum();
|
|
466
|
+
const { solana } = useSolana();
|
|
467
|
+
const { ethereum } = useEthereum();
|
|
120
468
|
|
|
121
|
-
// 1. Connect first
|
|
469
|
+
// 1. Connect first (provider parameter is required)
|
|
122
470
|
const handleConnect = async () => {
|
|
123
|
-
await connect();
|
|
471
|
+
await connect({ provider: "injected" });
|
|
124
472
|
};
|
|
125
473
|
|
|
126
474
|
// 2. Then use chain-specific operations
|
|
@@ -136,39 +484,94 @@ function WalletExample() {
|
|
|
136
484
|
|
|
137
485
|
### Connection Options
|
|
138
486
|
|
|
139
|
-
|
|
487
|
+
The `connect()` method requires a `provider` parameter and automatically switches between providers based on the authentication method you specify:
|
|
140
488
|
|
|
141
489
|
```tsx
|
|
142
490
|
const { connect } = useConnect();
|
|
143
491
|
|
|
144
|
-
//
|
|
145
|
-
|
|
492
|
+
// Connect with injected provider (Phantom extension)
|
|
493
|
+
// Automatically switches to injected provider if not already using it
|
|
494
|
+
await connect({
|
|
495
|
+
provider: "injected",
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// Connect with Google authentication (embedded provider)
|
|
499
|
+
// Automatically switches to embedded provider if not already using it
|
|
500
|
+
await connect({
|
|
501
|
+
provider: "google",
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// Connect with Apple authentication (embedded provider)
|
|
505
|
+
// Automatically switches to embedded provider if not already using it
|
|
506
|
+
await connect({
|
|
507
|
+
provider: "apple",
|
|
508
|
+
});
|
|
146
509
|
|
|
147
|
-
//
|
|
510
|
+
// Connect with Phantom authentication (embedded provider)
|
|
511
|
+
// Uses Phantom extension or mobile app for authentication
|
|
512
|
+
// Automatically switches to embedded provider if not already using it
|
|
148
513
|
await connect({
|
|
149
|
-
|
|
150
|
-
provider: "google",
|
|
151
|
-
},
|
|
514
|
+
provider: "phantom",
|
|
152
515
|
});
|
|
153
516
|
|
|
154
|
-
//
|
|
517
|
+
// Connect with JWT authentication (embedded provider)
|
|
155
518
|
await connect({
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
},
|
|
519
|
+
provider: "jwt",
|
|
520
|
+
jwtToken: "your-jwt-token",
|
|
159
521
|
});
|
|
160
522
|
```
|
|
161
523
|
|
|
162
|
-
##
|
|
524
|
+
## SDK Initialization
|
|
525
|
+
|
|
526
|
+
The SDK provides an `isLoading` state to track when initialization and autoconnect are in progress. This is useful for showing loading states before your app is ready.
|
|
527
|
+
|
|
528
|
+
```tsx
|
|
529
|
+
import { useConnect, usePhantom } from "@phantom/react-sdk";
|
|
530
|
+
|
|
531
|
+
function App() {
|
|
532
|
+
const { isLoading } = usePhantom();
|
|
533
|
+
const { connect } = useConnect();
|
|
534
|
+
|
|
535
|
+
// Show loading state while SDK initializes
|
|
536
|
+
if (isLoading) {
|
|
537
|
+
return (
|
|
538
|
+
<div>
|
|
539
|
+
<h1>Initializing Phantom SDK...</h1>
|
|
540
|
+
<p>Please wait...</p>
|
|
541
|
+
</div>
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// SDK is ready
|
|
546
|
+
return (
|
|
547
|
+
<div>
|
|
548
|
+
<h1>Welcome!</h1>
|
|
549
|
+
<button onClick={() => connect({ provider: "injected" })}>Connect Wallet</button>
|
|
550
|
+
</div>
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
## Authentication Providers
|
|
556
|
+
|
|
557
|
+
The SDK supports multiple authentication providers that you configure via the `providers` array:
|
|
558
|
+
|
|
559
|
+
### Available Providers
|
|
560
|
+
|
|
561
|
+
- **`"injected"`** - Phantom browser extension
|
|
562
|
+
- **`"google"`** - Google OAuth
|
|
563
|
+
- **`"apple"`** - Apple ID
|
|
564
|
+
- **`"phantom"`** - Phantom Login
|
|
565
|
+
- **`"deeplink"`** - Deeplink to Phantom mobile app (only renders on mobile devices)
|
|
163
566
|
|
|
164
|
-
###
|
|
567
|
+
### Configuration Examples
|
|
165
568
|
|
|
166
|
-
|
|
569
|
+
**Browser Extension Only**
|
|
167
570
|
|
|
168
571
|
```tsx
|
|
169
572
|
<PhantomProvider
|
|
170
573
|
config={{
|
|
171
|
-
|
|
574
|
+
providers: ["injected"], // Only allow browser extension
|
|
172
575
|
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
173
576
|
}}
|
|
174
577
|
>
|
|
@@ -176,11 +579,39 @@ Uses the Phantom browser extension installed by the user.
|
|
|
176
579
|
</PhantomProvider>
|
|
177
580
|
```
|
|
178
581
|
|
|
179
|
-
|
|
582
|
+
**Multiple Authentication Methods**
|
|
180
583
|
|
|
181
|
-
|
|
584
|
+
```tsx
|
|
585
|
+
<PhantomProvider
|
|
586
|
+
config={{
|
|
587
|
+
providers: ["google", "apple", "phantom", "injected", "deeplink"], // Allow all methods
|
|
588
|
+
appId: "your-app-id", // Required for embedded providers
|
|
589
|
+
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
590
|
+
}}
|
|
591
|
+
>
|
|
592
|
+
<YourApp />
|
|
593
|
+
</PhantomProvider>
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
**Mobile Deeplink Support**
|
|
182
597
|
|
|
183
|
-
|
|
598
|
+
The `"deeplink"` provider enables a button that opens the Phantom mobile app on mobile devices. This button only appears on mobile devices when the Phantom browser extension is not installed. When clicked, it redirects users to the Phantom mobile app to complete authentication.
|
|
599
|
+
|
|
600
|
+
```tsx
|
|
601
|
+
<PhantomProvider
|
|
602
|
+
config={{
|
|
603
|
+
providers: ["google", "apple", "phantom", "deeplink"], // Include deeplink for mobile support
|
|
604
|
+
appId: "your-app-id", // Required for deeplink
|
|
605
|
+
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
606
|
+
}}
|
|
607
|
+
>
|
|
608
|
+
<YourApp />
|
|
609
|
+
</PhantomProvider>
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### Embedded Wallet Type
|
|
613
|
+
|
|
614
|
+
When using embedded providers (google, apple, phantom, etc.), you can specify the wallet type using `embeddedWalletType`. The default is `"user-wallet"`:
|
|
184
615
|
|
|
185
616
|
- **Uses Phantom authentication** - user logs in with existing account
|
|
186
617
|
- **Potentially funded** - brings existing wallet balance
|
|
@@ -189,16 +620,16 @@ Creates non-custodial wallets embedded in your application.
|
|
|
189
620
|
```tsx
|
|
190
621
|
<PhantomProvider
|
|
191
622
|
config={{
|
|
192
|
-
|
|
623
|
+
providers: ["google", "apple", "phantom"],
|
|
193
624
|
appId: "your-app-id",
|
|
194
625
|
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
626
|
+
embeddedWalletType: "user-wallet", // default, can be omitted
|
|
195
627
|
}}
|
|
196
628
|
>
|
|
197
629
|
<YourApp />
|
|
198
630
|
</PhantomProvider>
|
|
199
631
|
```
|
|
200
632
|
|
|
201
|
-
|
|
202
633
|
## Available Hooks
|
|
203
634
|
|
|
204
635
|
### Core Connection Hooks
|
|
@@ -211,17 +642,22 @@ Connect to wallet:
|
|
|
211
642
|
import { useConnect } from "@phantom/react-sdk";
|
|
212
643
|
|
|
213
644
|
function ConnectButton() {
|
|
214
|
-
const { connect, isConnecting, error } = useConnect();
|
|
645
|
+
const { connect, isConnecting, isLoading, error } = useConnect();
|
|
215
646
|
|
|
216
647
|
const handleConnect = async () => {
|
|
217
648
|
try {
|
|
218
|
-
const {
|
|
649
|
+
const { addresses } = await connect({ provider: "injected" });
|
|
219
650
|
console.log("Connected addresses:", addresses);
|
|
220
651
|
} catch (err) {
|
|
221
652
|
console.error("Failed to connect:", err);
|
|
222
653
|
}
|
|
223
654
|
};
|
|
224
655
|
|
|
656
|
+
// Wait for SDK to finish initializing before showing connect button
|
|
657
|
+
if (isLoading) {
|
|
658
|
+
return <div>Loading...</div>;
|
|
659
|
+
}
|
|
660
|
+
|
|
225
661
|
return (
|
|
226
662
|
<button onClick={handleConnect} disabled={isConnecting}>
|
|
227
663
|
{isConnecting ? "Connecting..." : "Connect Wallet"}
|
|
@@ -305,6 +741,29 @@ function ExtensionStatus() {
|
|
|
305
741
|
}
|
|
306
742
|
```
|
|
307
743
|
|
|
744
|
+
#### useIsPhantomLoginAvailable
|
|
745
|
+
|
|
746
|
+
Check if Phantom Login is available (requires extension installed and `phantom_login` feature support):
|
|
747
|
+
|
|
748
|
+
```tsx
|
|
749
|
+
import { useIsPhantomLoginAvailable } from "@phantom/react-sdk";
|
|
750
|
+
|
|
751
|
+
function PhantomLoginButton() {
|
|
752
|
+
const { isLoading, isAvailable } = useIsPhantomLoginAvailable();
|
|
753
|
+
const { connect } = useConnect();
|
|
754
|
+
|
|
755
|
+
if (isLoading) {
|
|
756
|
+
return <div>Checking Phantom Login availability...</div>;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (!isAvailable) {
|
|
760
|
+
return null; // Don't show button if Phantom Login is not available
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return <button onClick={() => connect({ provider: "phantom" })}>Login with Phantom</button>;
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
308
767
|
### Chain-Specific Hooks
|
|
309
768
|
|
|
310
769
|
#### useSolana
|
|
@@ -316,7 +775,12 @@ import { useSolana } from "@phantom/react-sdk";
|
|
|
316
775
|
import { VersionedTransaction, TransactionMessage, SystemProgram, PublicKey, Connection } from "@solana/web3.js";
|
|
317
776
|
|
|
318
777
|
function SolanaOperations() {
|
|
319
|
-
const solana = useSolana();
|
|
778
|
+
const { solana, isAvailable } = useSolana();
|
|
779
|
+
|
|
780
|
+
// Check if Solana is available before using it
|
|
781
|
+
if (!isAvailable) {
|
|
782
|
+
return <div>Solana is not available for the current wallet</div>;
|
|
783
|
+
}
|
|
320
784
|
|
|
321
785
|
const signMessage = async () => {
|
|
322
786
|
const signature = await solana.signMessage("Hello Solana!");
|
|
@@ -371,7 +835,19 @@ function SolanaOperations() {
|
|
|
371
835
|
- `switchNetwork(network)` - Switch between mainnet/devnet
|
|
372
836
|
- `getPublicKey()` - Get current public key
|
|
373
837
|
- `isConnected` - Connection status
|
|
374
|
-
- `isAvailable` - Provider availability
|
|
838
|
+
- `isAvailable` - Provider availability (see note below)
|
|
839
|
+
|
|
840
|
+
**Note on `isAvailable`:**
|
|
841
|
+
|
|
842
|
+
The `isAvailable` property indicates whether the Solana chain is available for the currently connected wallet:
|
|
843
|
+
|
|
844
|
+
- **For embedded wallets** (Google, Apple, Phantom Login, etc.): `isAvailable` will be `true` for all networks configured in your `addressTypes` array, as embedded wallets support all configured networks.
|
|
845
|
+
|
|
846
|
+
- **For Phantom injected wallet**: `isAvailable` will be `true` for all networks configured in your `addressTypes` array, as Phantom supports multiple networks.
|
|
847
|
+
|
|
848
|
+
- **For other injected wallets** (discovered via Wallet Standard or EIP-6963): `isAvailable` depends on which networks the specific wallet supports. For example, if you connect to a wallet that only supports Ethereum, `isAvailable` will be `false` for Solana even if Solana is in your `addressTypes` configuration.
|
|
849
|
+
|
|
850
|
+
Always check `isAvailable` before attempting to use chain-specific methods when working with injected wallets that may not support all networks.
|
|
375
851
|
|
|
376
852
|
#### useEthereum
|
|
377
853
|
|
|
@@ -381,7 +857,12 @@ Hook for Ethereum chain operations:
|
|
|
381
857
|
import { useEthereum } from "@phantom/react-sdk";
|
|
382
858
|
|
|
383
859
|
function EthereumOperations() {
|
|
384
|
-
const ethereum = useEthereum();
|
|
860
|
+
const { ethereum, isAvailable } = useEthereum();
|
|
861
|
+
|
|
862
|
+
// Check if Ethereum is available before using it
|
|
863
|
+
if (!isAvailable) {
|
|
864
|
+
return <div>Ethereum is not available for the current wallet</div>;
|
|
865
|
+
}
|
|
385
866
|
|
|
386
867
|
const signPersonalMessage = async () => {
|
|
387
868
|
const accounts = await ethereum.getAccounts();
|
|
@@ -466,11 +947,71 @@ function EthereumOperations() {
|
|
|
466
947
|
- `signTypedData(typedData)` - Sign EIP-712 typed data
|
|
467
948
|
- `signTransaction(transaction)` - Sign transaction without sending
|
|
468
949
|
- `sendTransaction(transaction)` - Sign and send transaction
|
|
469
|
-
- `switchChain(chainId)` - Switch chains
|
|
950
|
+
- `switchChain(chainId)` - Switch chains (accepts chain ID as number or a hex string)
|
|
470
951
|
- `getChainId()` - Get current chain ID
|
|
471
952
|
- `getAccounts()` - Get connected accounts
|
|
472
953
|
- `isConnected` - Connection status
|
|
473
|
-
- `isAvailable` - Provider availability
|
|
954
|
+
- `isAvailable` - Provider availability (see note below)
|
|
955
|
+
|
|
956
|
+
**Note on `isAvailable`:**
|
|
957
|
+
|
|
958
|
+
The `isAvailable` property indicates whether the Ethereum chain is available for the currently connected wallet:
|
|
959
|
+
|
|
960
|
+
- **For embedded wallets** (Google, Apple, Phantom Login, etc.): `isAvailable` will be `true` for all networks configured in your `addressTypes` array, as embedded wallets support all configured networks.
|
|
961
|
+
|
|
962
|
+
- **For Phantom injected wallet**: `isAvailable` will be `true` for all networks configured in your `addressTypes` array, as Phantom supports multiple networks.
|
|
963
|
+
|
|
964
|
+
- **For other injected wallets** (discovered via Wallet Standard or EIP-6963): `isAvailable` depends on which networks the specific wallet supports. For example, if you connect to a wallet that only supports Solana, `isAvailable` will be `false` for Ethereum even if Ethereum is in your `addressTypes` configuration.
|
|
965
|
+
|
|
966
|
+
Always check `isAvailable` before attempting to use chain-specific methods when working with injected wallets that may not support all networks.
|
|
967
|
+
|
|
968
|
+
**Supported EVM Networks:**
|
|
969
|
+
|
|
970
|
+
| Network | Chain ID | Usage |
|
|
971
|
+
| ---------------- | ---------- | -------------------------------- |
|
|
972
|
+
| Ethereum Mainnet | `1` | `ethereum.switchChain(1)` |
|
|
973
|
+
| Ethereum Sepolia | `11155111` | `ethereum.switchChain(11155111)` |
|
|
974
|
+
| Polygon Mainnet | `137` | `ethereum.switchChain(137)` |
|
|
975
|
+
| Polygon Amoy | `80002` | `ethereum.switchChain(80002)` |
|
|
976
|
+
| Base Mainnet | `8453` | `ethereum.switchChain(8453)` |
|
|
977
|
+
| Base Sepolia | `84532` | `ethereum.switchChain(84532)` |
|
|
978
|
+
| Arbitrum One | `42161` | `ethereum.switchChain(42161)` |
|
|
979
|
+
| Arbitrum Sepolia | `421614` | `ethereum.switchChain(421614)` |
|
|
980
|
+
| Monad Mainnet | `143` | `ethereum.switchChain(143)` |
|
|
981
|
+
| Monad Testnet | `10143` | `ethereum.switchChain(10143)` |
|
|
982
|
+
|
|
983
|
+
### Wallet Discovery Hook
|
|
984
|
+
|
|
985
|
+
#### useDiscoveredWallets
|
|
986
|
+
|
|
987
|
+
Hook to get discovered injected wallets with automatic loading and error states. Discovers wallets using Wallet Standard (Solana) and EIP-6963 (Ethereum) standards.
|
|
988
|
+
|
|
989
|
+
```tsx
|
|
990
|
+
import { useDiscoveredWallets } from "@phantom/react-sdk";
|
|
991
|
+
|
|
992
|
+
function WalletSelector() {
|
|
993
|
+
const { wallets, isLoading, error, refetch } = useDiscoveredWallets();
|
|
994
|
+
|
|
995
|
+
// wallets: InjectedWalletInfo[] - Array of discovered wallets
|
|
996
|
+
// isLoading: boolean - Loading state during discovery
|
|
997
|
+
// error: Error | null - Error state if discovery fails
|
|
998
|
+
// refetch: () => Promise<void> - Function to manually trigger discovery
|
|
999
|
+
}
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
**Returns:**
|
|
1003
|
+
|
|
1004
|
+
- `wallets: InjectedWalletInfo[]` - Array of discovered wallet information
|
|
1005
|
+
- `isLoading: boolean` - `true` while discovery is in progress
|
|
1006
|
+
- `error: Error | null` - Error object if discovery fails, `null` otherwise
|
|
1007
|
+
- `refetch: () => Promise<void>` - Async function to manually refresh the wallet list
|
|
1008
|
+
|
|
1009
|
+
**Behavior:**
|
|
1010
|
+
|
|
1011
|
+
- Automatically fetches discovered wallets when the SDK becomes available
|
|
1012
|
+
- If no wallets are found in the registry, triggers async `discoverWallets()` to discover them
|
|
1013
|
+
- Wallets are filtered based on the `addressTypes` configured in `PhantomProvider`
|
|
1014
|
+
- Phantom wallet is automatically included if available
|
|
474
1015
|
|
|
475
1016
|
### Auto-Confirm Hook (Injected Provider Only)
|
|
476
1017
|
|
|
@@ -614,7 +1155,7 @@ import { VersionedTransaction, TransactionMessage, SystemProgram, PublicKey, Con
|
|
|
614
1155
|
import { useSolana } from "@phantom/react-sdk";
|
|
615
1156
|
|
|
616
1157
|
function SolanaExample() {
|
|
617
|
-
const solana = useSolana();
|
|
1158
|
+
const { solana } = useSolana();
|
|
618
1159
|
|
|
619
1160
|
const sendTransaction = async () => {
|
|
620
1161
|
// Get recent blockhash
|
|
@@ -662,7 +1203,7 @@ import {
|
|
|
662
1203
|
import { useSolana } from "@phantom/react-sdk";
|
|
663
1204
|
|
|
664
1205
|
function SolanaKitExample() {
|
|
665
|
-
const solana = useSolana();
|
|
1206
|
+
const { solana } = useSolana();
|
|
666
1207
|
|
|
667
1208
|
const sendTransaction = async () => {
|
|
668
1209
|
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
|
|
@@ -693,7 +1234,7 @@ import { parseEther, parseGwei, encodeFunctionData } from "viem";
|
|
|
693
1234
|
import { useEthereum } from "@phantom/react-sdk";
|
|
694
1235
|
|
|
695
1236
|
function EthereumExample() {
|
|
696
|
-
const ethereum = useEthereum();
|
|
1237
|
+
const { ethereum } = useEthereum();
|
|
697
1238
|
|
|
698
1239
|
const sendEth = async () => {
|
|
699
1240
|
const result = await ethereum.sendTransaction({
|
|
@@ -733,36 +1274,45 @@ function EthereumExample() {
|
|
|
733
1274
|
|
|
734
1275
|
Quick reference of all available hooks:
|
|
735
1276
|
|
|
736
|
-
| Hook
|
|
737
|
-
|
|
|
738
|
-
| `useConnect`
|
|
739
|
-
| `
|
|
740
|
-
| `
|
|
741
|
-
| `
|
|
742
|
-
| `
|
|
743
|
-
| `
|
|
744
|
-
| `
|
|
745
|
-
| `
|
|
1277
|
+
| Hook | Purpose | Returns |
|
|
1278
|
+
| ---------------------------- | --------------------------------------- | --------------------------------------------------- |
|
|
1279
|
+
| `useConnect` | Connect to wallet | `{ connect, isConnecting, error }` |
|
|
1280
|
+
| `useModal` | Control connection modal | `{ open, close, isOpened }` |
|
|
1281
|
+
| `useAccounts` | Get wallet addresses | `WalletAddress[]` or `null` |
|
|
1282
|
+
| `useIsExtensionInstalled` | Check extension status | `{ isLoading, isInstalled }` |
|
|
1283
|
+
| `useIsPhantomLoginAvailable` | Check Phantom Login availability | `{ isLoading, isAvailable }` |
|
|
1284
|
+
| `useDisconnect` | Disconnect from wallet | `{ disconnect, isDisconnecting }` |
|
|
1285
|
+
| `useAutoConfirm` | Auto-confirm management (injected only) | `{ enable, disable, status, supportedChains, ... }` |
|
|
1286
|
+
| `useDiscoveredWallets` | Get discovered injected wallets | `{ wallets, isLoading, error, refetch }` |
|
|
1287
|
+
| `useSolana` | Solana chain operations | `{ signMessage, signAndSendTransaction, ... }` |
|
|
1288
|
+
| `useEthereum` | Ethereum chain operations | `{ signPersonalMessage, sendTransaction, ... }` |
|
|
1289
|
+
| `useTheme` | Access current theme | `PhantomTheme` |
|
|
1290
|
+
| `usePhantom` | Get provider context | `{ isConnected, isReady }` |
|
|
746
1291
|
|
|
747
1292
|
## Configuration Reference
|
|
748
1293
|
|
|
749
1294
|
```typescript
|
|
750
1295
|
interface PhantomSDKConfig {
|
|
751
|
-
|
|
1296
|
+
// List of allowed authentication providers (REQUIRED)
|
|
1297
|
+
providers: AuthProviderType[]; // e.g., ["google", "apple", "phantom", "injected"]
|
|
1298
|
+
|
|
752
1299
|
addressTypes?: [AddressType, ...AddressType[]]; // Networks to enable (e.g., [AddressType.solana])
|
|
753
1300
|
|
|
754
|
-
// Required
|
|
755
|
-
appId
|
|
756
|
-
|
|
1301
|
+
// Required when using embedded providers (google, apple, phantom)
|
|
1302
|
+
appId?: string; // Your app ID from phantom.com/portal
|
|
1303
|
+
|
|
757
1304
|
// Optional configuration
|
|
758
1305
|
apiBaseUrl?: string; // Phantom API base URL (optional, has default)
|
|
759
1306
|
authOptions?: {
|
|
760
1307
|
authUrl?: string; // Custom auth URL (optional, defaults to "https://connect.phantom.app/login")
|
|
761
1308
|
redirectUrl?: string; // Custom redirect URL after authentication (optional)
|
|
762
1309
|
};
|
|
763
|
-
embeddedWalletType?: "user-wallet"; // Wallet type (optional, defaults to "user-wallet"
|
|
764
|
-
autoConnect?: boolean; // Auto-connect to existing session
|
|
1310
|
+
embeddedWalletType?: "user-wallet"; // Wallet type (optional, defaults to "user-wallet")
|
|
1311
|
+
autoConnect?: boolean; // Auto-connect to existing session (default: true when embedded providers used)
|
|
765
1312
|
}
|
|
1313
|
+
|
|
1314
|
+
// Valid provider types
|
|
1315
|
+
type AuthProviderType = "google" | "apple" | "phantom" | "injected";
|
|
766
1316
|
```
|
|
767
1317
|
|
|
768
1318
|
## Debug Configuration
|
|
@@ -792,8 +1342,9 @@ function App() {
|
|
|
792
1342
|
|
|
793
1343
|
// SDK configuration - static, won't change when debug settings change
|
|
794
1344
|
const config: PhantomSDKConfig = {
|
|
795
|
-
|
|
1345
|
+
providers: ["google", "apple", "phantom"],
|
|
796
1346
|
appId: "your-app-id",
|
|
1347
|
+
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
797
1348
|
// ... other config
|
|
798
1349
|
};
|
|
799
1350
|
|