@phantom/react-sdk 0.3.9 → 1.0.0-beta.1
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 +409 -267
- package/dist/index.d.ts +70 -16
- package/dist/index.js +300 -110
- package/dist/index.mjs +297 -107
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @phantom/react-sdk
|
|
2
2
|
|
|
3
|
-
React hooks for integrating Phantom wallet functionality into React applications with
|
|
3
|
+
React hooks for integrating Phantom wallet functionality into React applications with chain-specific operations.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -16,8 +16,6 @@ Install additional dependencies based on the networks you want to support:
|
|
|
16
16
|
| --------------- | ---------------------------------- |
|
|
17
17
|
| Solana | `@solana/web3.js` OR `@solana/kit` |
|
|
18
18
|
| Ethereum/EVM | `viem` |
|
|
19
|
-
| Bitcoin | `bitcoinjs-lib` |
|
|
20
|
-
| Sui | `@mysten/sui.js` |
|
|
21
19
|
|
|
22
20
|
**Example for Solana + Ethereum support (using @solana/web3.js):**
|
|
23
21
|
|
|
@@ -31,34 +29,65 @@ npm install @phantom/react-sdk @solana/web3.js viem
|
|
|
31
29
|
npm install @phantom/react-sdk @solana/kit viem
|
|
32
30
|
```
|
|
33
31
|
|
|
34
|
-
For complete dependency information and bundle optimization tips, see the [@phantom/browser-sdk documentation](../browser-sdk/README.md#bundle-optimization-tips).
|
|
35
|
-
|
|
36
32
|
## Quick Start
|
|
37
33
|
|
|
38
|
-
### Basic Setup
|
|
34
|
+
### Basic Setup with Chain-Specific Operations
|
|
39
35
|
|
|
40
36
|
```tsx
|
|
41
|
-
import { PhantomProvider } from "@phantom/react-sdk";
|
|
42
|
-
import { AddressType } from "@phantom/
|
|
37
|
+
import { PhantomProvider, useConnect, useSolana, useEthereum } from "@phantom/react-sdk";
|
|
38
|
+
import { AddressType } from "@phantom/browser-sdk";
|
|
43
39
|
|
|
44
40
|
function App() {
|
|
45
41
|
return (
|
|
46
42
|
<PhantomProvider
|
|
47
43
|
config={{
|
|
48
44
|
providerType: "injected", // Uses Phantom browser extension
|
|
45
|
+
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
49
46
|
}}
|
|
50
47
|
>
|
|
51
|
-
<
|
|
48
|
+
<WalletComponent />
|
|
52
49
|
</PhantomProvider>
|
|
53
50
|
);
|
|
54
51
|
}
|
|
52
|
+
|
|
53
|
+
function WalletComponent() {
|
|
54
|
+
const { connect, isConnecting } = useConnect();
|
|
55
|
+
const solana = useSolana();
|
|
56
|
+
const ethereum = useEthereum();
|
|
57
|
+
|
|
58
|
+
const handleConnect = async () => {
|
|
59
|
+
const { addresses } = await connect();
|
|
60
|
+
console.log("Connected addresses:", addresses);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const signSolanaMessage = async () => {
|
|
64
|
+
const signature = await solana.signMessage("Hello Solana!");
|
|
65
|
+
console.log("Solana signature:", signature);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const signEthereumMessage = async () => {
|
|
69
|
+
const accounts = await ethereum.getAccounts();
|
|
70
|
+
const signature = await ethereum.signPersonalMessage("Hello Ethereum!", accounts[0]);
|
|
71
|
+
console.log("Ethereum signature:", signature);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div>
|
|
76
|
+
<button onClick={handleConnect} disabled={isConnecting}>
|
|
77
|
+
{isConnecting ? "Connecting..." : "Connect Wallet"}
|
|
78
|
+
</button>
|
|
79
|
+
<button onClick={signSolanaMessage}>Sign Solana Message</button>
|
|
80
|
+
<button onClick={signEthereumMessage}>Sign Ethereum Message</button>
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
55
84
|
```
|
|
56
85
|
|
|
57
86
|
### Embedded Wallet Setup
|
|
58
87
|
|
|
59
88
|
```tsx
|
|
60
89
|
import { PhantomProvider } from "@phantom/react-sdk";
|
|
61
|
-
import { AddressType } from "@phantom/
|
|
90
|
+
import { AddressType } from "@phantom/browser-sdk";
|
|
62
91
|
|
|
63
92
|
function App() {
|
|
64
93
|
return (
|
|
@@ -68,7 +97,6 @@ function App() {
|
|
|
68
97
|
embeddedWalletType: "app-wallet", // or 'user-wallet'
|
|
69
98
|
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
70
99
|
apiBaseUrl: "https://api.phantom.app/v1/wallets",
|
|
71
|
-
organizationId: "your-org-id",
|
|
72
100
|
}}
|
|
73
101
|
>
|
|
74
102
|
<YourApp />
|
|
@@ -77,6 +105,61 @@ function App() {
|
|
|
77
105
|
}
|
|
78
106
|
```
|
|
79
107
|
|
|
108
|
+
## Connection Flow
|
|
109
|
+
|
|
110
|
+
The React SDK follows a clear connection pattern:
|
|
111
|
+
|
|
112
|
+
1. **Provider Setup**: Wrap your app with `PhantomProvider`
|
|
113
|
+
2. **Connection**: Use `useConnect()` to establish wallet connection
|
|
114
|
+
3. **Chain Operations**: Use chain-specific hooks (`useSolana()`, `useEthereum()`) for transactions and signing
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
function WalletExample() {
|
|
118
|
+
const { connect } = useConnect();
|
|
119
|
+
const solana = useSolana();
|
|
120
|
+
const ethereum = useEthereum();
|
|
121
|
+
|
|
122
|
+
// 1. Connect first
|
|
123
|
+
const handleConnect = async () => {
|
|
124
|
+
await connect();
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// 2. Then use chain-specific operations
|
|
128
|
+
const sendSolanaTransaction = async () => {
|
|
129
|
+
const result = await solana.signAndSendTransaction(transaction);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const sendEthereumTransaction = async () => {
|
|
133
|
+
const result = await ethereum.sendTransaction(transaction);
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Connection Options
|
|
139
|
+
|
|
140
|
+
For embedded user-wallets, you can specify authentication providers:
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
const { connect } = useConnect();
|
|
144
|
+
|
|
145
|
+
// Default: Show provider selection screen
|
|
146
|
+
await connect();
|
|
147
|
+
|
|
148
|
+
// Google authentication (skips provider selection)
|
|
149
|
+
await connect({
|
|
150
|
+
authOptions: {
|
|
151
|
+
provider: "google",
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Apple authentication (skips provider selection)
|
|
156
|
+
await connect({
|
|
157
|
+
authOptions: {
|
|
158
|
+
provider: "apple",
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
80
163
|
## Provider Types
|
|
81
164
|
|
|
82
165
|
### Injected Provider
|
|
@@ -87,6 +170,7 @@ Uses the Phantom browser extension installed by the user.
|
|
|
87
170
|
<PhantomProvider
|
|
88
171
|
config={{
|
|
89
172
|
providerType: "injected",
|
|
173
|
+
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
90
174
|
}}
|
|
91
175
|
>
|
|
92
176
|
<YourApp />
|
|
@@ -110,7 +194,6 @@ Creates non-custodial wallets embedded in your application.
|
|
|
110
194
|
embeddedWalletType: "app-wallet",
|
|
111
195
|
addressTypes: [AddressType.solana],
|
|
112
196
|
apiBaseUrl: "https://api.phantom.app/v1/wallets",
|
|
113
|
-
organizationId: "your-org-id",
|
|
114
197
|
}}
|
|
115
198
|
>
|
|
116
199
|
<YourApp />
|
|
@@ -130,7 +213,6 @@ Creates non-custodial wallets embedded in your application.
|
|
|
130
213
|
embeddedWalletType: "user-wallet",
|
|
131
214
|
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
132
215
|
apiBaseUrl: "https://api.phantom.app/v1/wallets",
|
|
133
|
-
organizationId: "your-org-id",
|
|
134
216
|
}}
|
|
135
217
|
>
|
|
136
218
|
<YourApp />
|
|
@@ -148,7 +230,6 @@ When using `AddressType.solana`, you can choose between two Solana libraries:
|
|
|
148
230
|
addressTypes: [AddressType.solana],
|
|
149
231
|
solanaProvider: "web3js", // or 'kit'
|
|
150
232
|
apiBaseUrl: "https://api.phantom.app/v1/wallets",
|
|
151
|
-
organizationId: "your-org-id",
|
|
152
233
|
}}
|
|
153
234
|
>
|
|
154
235
|
<YourApp />
|
|
@@ -167,7 +248,9 @@ When using `AddressType.solana`, you can choose between two Solana libraries:
|
|
|
167
248
|
|
|
168
249
|
## Available Hooks
|
|
169
250
|
|
|
170
|
-
###
|
|
251
|
+
### Core Connection Hooks
|
|
252
|
+
|
|
253
|
+
#### useConnect
|
|
171
254
|
|
|
172
255
|
Connect to wallet:
|
|
173
256
|
|
|
@@ -175,7 +258,7 @@ Connect to wallet:
|
|
|
175
258
|
import { useConnect } from "@phantom/react-sdk";
|
|
176
259
|
|
|
177
260
|
function ConnectButton() {
|
|
178
|
-
const { connect,
|
|
261
|
+
const { connect, isConnecting, error } = useConnect();
|
|
179
262
|
|
|
180
263
|
const handleConnect = async () => {
|
|
181
264
|
try {
|
|
@@ -187,14 +270,14 @@ function ConnectButton() {
|
|
|
187
270
|
};
|
|
188
271
|
|
|
189
272
|
return (
|
|
190
|
-
<button onClick={handleConnect} disabled={
|
|
191
|
-
{
|
|
273
|
+
<button onClick={handleConnect} disabled={isConnecting}>
|
|
274
|
+
{isConnecting ? "Connecting..." : "Connect Wallet"}
|
|
192
275
|
</button>
|
|
193
276
|
);
|
|
194
277
|
}
|
|
195
278
|
```
|
|
196
279
|
|
|
197
|
-
|
|
280
|
+
#### useAccounts
|
|
198
281
|
|
|
199
282
|
Get connected wallet addresses:
|
|
200
283
|
|
|
@@ -220,7 +303,25 @@ function WalletAddresses() {
|
|
|
220
303
|
}
|
|
221
304
|
```
|
|
222
305
|
|
|
223
|
-
|
|
306
|
+
#### useDisconnect
|
|
307
|
+
|
|
308
|
+
Disconnect from wallet:
|
|
309
|
+
|
|
310
|
+
```tsx
|
|
311
|
+
import { useDisconnect } from "@phantom/react-sdk";
|
|
312
|
+
|
|
313
|
+
function DisconnectButton() {
|
|
314
|
+
const { disconnect, isDisconnecting } = useDisconnect();
|
|
315
|
+
|
|
316
|
+
return (
|
|
317
|
+
<button onClick={disconnect} disabled={isDisconnecting}>
|
|
318
|
+
{isDisconnecting ? "Disconnecting..." : "Disconnect"}
|
|
319
|
+
</button>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### useIsExtensionInstalled
|
|
224
325
|
|
|
225
326
|
Check if the Phantom browser extension is installed (for injected provider):
|
|
226
327
|
|
|
@@ -251,197 +352,293 @@ function ExtensionStatus() {
|
|
|
251
352
|
}
|
|
252
353
|
```
|
|
253
354
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
- **Session-based caching**: Result is cached during the browser session to avoid redundant checks
|
|
257
|
-
- **Automatic detection**: Runs automatically when the hook is first used
|
|
258
|
-
- **Loading states**: Provides `isLoading` during the initial check
|
|
259
|
-
- **Performance optimized**: Subsequent calls return cached result instantly
|
|
355
|
+
### Chain-Specific Hooks
|
|
260
356
|
|
|
261
|
-
|
|
357
|
+
#### useSolana
|
|
262
358
|
|
|
263
|
-
|
|
264
|
-
- Conditionally render UI based on extension availability
|
|
265
|
-
- Provide fallback options when extension is not installed
|
|
266
|
-
|
|
267
|
-
### useDisconnect
|
|
268
|
-
|
|
269
|
-
Disconnect from wallet:
|
|
359
|
+
Hook for Solana chain operations:
|
|
270
360
|
|
|
271
361
|
```tsx
|
|
272
|
-
import {
|
|
362
|
+
import { useSolana } from "@phantom/react-sdk";
|
|
363
|
+
import { VersionedTransaction, TransactionMessage, SystemProgram, PublicKey, Connection } from "@solana/web3.js";
|
|
273
364
|
|
|
274
|
-
function
|
|
275
|
-
const
|
|
365
|
+
function SolanaOperations() {
|
|
366
|
+
const solana = useSolana();
|
|
276
367
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
```
|
|
368
|
+
const signMessage = async () => {
|
|
369
|
+
const signature = await solana.signMessage("Hello Solana!");
|
|
370
|
+
console.log("Signature:", signature);
|
|
371
|
+
};
|
|
284
372
|
|
|
285
|
-
|
|
373
|
+
const signAndSendTransaction = async () => {
|
|
374
|
+
// Create transaction
|
|
375
|
+
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
376
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
286
377
|
|
|
287
|
-
|
|
378
|
+
const fromAddress = await solana.getPublicKey();
|
|
379
|
+
const transferInstruction = SystemProgram.transfer({
|
|
380
|
+
fromPubkey: new PublicKey(fromAddress),
|
|
381
|
+
toPubkey: new PublicKey(toAddress),
|
|
382
|
+
lamports: 1000000, // 0.001 SOL
|
|
383
|
+
});
|
|
288
384
|
|
|
289
|
-
|
|
290
|
-
|
|
385
|
+
const messageV0 = new TransactionMessage({
|
|
386
|
+
payerKey: new PublicKey(fromAddress),
|
|
387
|
+
recentBlockhash: blockhash,
|
|
388
|
+
instructions: [transferInstruction],
|
|
389
|
+
}).compileToV0Message();
|
|
291
390
|
|
|
292
|
-
|
|
293
|
-
const { signMessage, isLoading, error } = useSignMessage();
|
|
391
|
+
const transaction = new VersionedTransaction(messageV0);
|
|
294
392
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
} catch (err) {
|
|
303
|
-
console.error("Failed to sign:", err);
|
|
304
|
-
}
|
|
393
|
+
// Sign and send
|
|
394
|
+
const result = await solana.signAndSendTransaction(transaction);
|
|
395
|
+
console.log("Transaction sent:", result.hash);
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
const switchNetwork = async () => {
|
|
399
|
+
await solana.switchNetwork("devnet");
|
|
305
400
|
};
|
|
306
401
|
|
|
307
402
|
return (
|
|
308
|
-
<
|
|
309
|
-
{
|
|
310
|
-
|
|
403
|
+
<div>
|
|
404
|
+
<button onClick={signMessage}>Sign Message</button>
|
|
405
|
+
<button onClick={signAndSendTransaction}>Send Transaction</button>
|
|
406
|
+
<button onClick={switchNetwork}>Switch to Devnet</button>
|
|
407
|
+
<p>Connected: {solana.isConnected ? "Yes" : "No"}</p>
|
|
408
|
+
</div>
|
|
311
409
|
);
|
|
312
410
|
}
|
|
313
411
|
```
|
|
314
412
|
|
|
315
|
-
|
|
413
|
+
**Available methods:**
|
|
414
|
+
|
|
415
|
+
- `signMessage(message)` - Sign a message
|
|
416
|
+
- `signTransaction(transaction)` - Sign without sending
|
|
417
|
+
- `signAndSendTransaction(transaction)` - Sign and send
|
|
418
|
+
- `switchNetwork(network)` - Switch between mainnet/devnet
|
|
419
|
+
- `getPublicKey()` - Get current public key
|
|
420
|
+
- `isConnected` - Connection status
|
|
421
|
+
- `isAvailable` - Provider availability
|
|
316
422
|
|
|
317
|
-
####
|
|
423
|
+
#### useEthereum
|
|
424
|
+
|
|
425
|
+
Hook for Ethereum chain operations:
|
|
318
426
|
|
|
319
427
|
```tsx
|
|
320
|
-
import {
|
|
321
|
-
import {
|
|
322
|
-
VersionedTransaction,
|
|
323
|
-
TransactionMessage,
|
|
324
|
-
SystemProgram,
|
|
325
|
-
PublicKey,
|
|
326
|
-
LAMPORTS_PER_SOL,
|
|
327
|
-
Connection,
|
|
328
|
-
} from "@solana/web3.js";
|
|
329
|
-
|
|
330
|
-
function SendSolanaTransaction() {
|
|
331
|
-
const { signAndSendTransaction, isLoading, error } = useSignAndSendTransaction();
|
|
332
|
-
|
|
333
|
-
const handleSend = async () => {
|
|
334
|
-
// Get recent blockhash
|
|
335
|
-
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
336
|
-
const { blockhash } = await connection.getLatestBlockhash();
|
|
428
|
+
import { useEthereum } from "@phantom/react-sdk";
|
|
337
429
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
fromPubkey: new PublicKey(fromAddress),
|
|
341
|
-
toPubkey: new PublicKey(toAddress),
|
|
342
|
-
lamports: 0.001 * LAMPORTS_PER_SOL,
|
|
343
|
-
});
|
|
430
|
+
function EthereumOperations() {
|
|
431
|
+
const ethereum = useEthereum();
|
|
344
432
|
|
|
345
|
-
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}).compileToV0Message();
|
|
433
|
+
const signPersonalMessage = async () => {
|
|
434
|
+
const accounts = await ethereum.getAccounts();
|
|
435
|
+
const signature = await ethereum.signPersonalMessage("Hello Ethereum!", accounts[0]);
|
|
436
|
+
console.log("Signature:", signature);
|
|
437
|
+
};
|
|
351
438
|
|
|
352
|
-
|
|
439
|
+
const signTypedData = async () => {
|
|
440
|
+
const accounts = await ethereum.getAccounts();
|
|
441
|
+
const typedData = {
|
|
442
|
+
types: {
|
|
443
|
+
EIP712Domain: [
|
|
444
|
+
{ name: "name", type: "string" },
|
|
445
|
+
{ name: "version", type: "string" },
|
|
446
|
+
{ name: "chainId", type: "uint256" },
|
|
447
|
+
{ name: "verifyingContract", type: "address" },
|
|
448
|
+
],
|
|
449
|
+
Mail: [
|
|
450
|
+
{ name: "from", type: "string" },
|
|
451
|
+
{ name: "to", type: "string" },
|
|
452
|
+
{ name: "contents", type: "string" },
|
|
453
|
+
],
|
|
454
|
+
},
|
|
455
|
+
primaryType: "Mail",
|
|
456
|
+
domain: {
|
|
457
|
+
name: "Ether Mail",
|
|
458
|
+
version: "1",
|
|
459
|
+
chainId: 1,
|
|
460
|
+
verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
|
|
461
|
+
},
|
|
462
|
+
message: {
|
|
463
|
+
from: "Alice",
|
|
464
|
+
to: "Bob",
|
|
465
|
+
contents: "Hello!",
|
|
466
|
+
},
|
|
467
|
+
};
|
|
353
468
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
469
|
+
const signature = await ethereum.signTypedData(typedData);
|
|
470
|
+
console.log("Typed data signature:", signature);
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
const sendTransaction = async () => {
|
|
474
|
+
const result = await ethereum.sendTransaction({
|
|
475
|
+
to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E",
|
|
476
|
+
value: "1000000000000000000", // 1 ETH in wei
|
|
477
|
+
gas: "21000",
|
|
478
|
+
});
|
|
479
|
+
console.log("Transaction sent:", result.hash);
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const switchChain = async () => {
|
|
483
|
+
await ethereum.switchChain(137); // Switch to Polygon
|
|
363
484
|
};
|
|
364
485
|
|
|
365
486
|
return (
|
|
366
|
-
<
|
|
367
|
-
{
|
|
368
|
-
|
|
487
|
+
<div>
|
|
488
|
+
<button onClick={signPersonalMessage}>Sign Personal Message</button>
|
|
489
|
+
<button onClick={signTypedData}>Sign Typed Data</button>
|
|
490
|
+
<button onClick={sendTransaction}>Send Transaction</button>
|
|
491
|
+
<button onClick={switchChain}>Switch to Polygon</button>
|
|
492
|
+
<p>Connected: {ethereum.isConnected ? "Yes" : "No"}</p>
|
|
493
|
+
</div>
|
|
369
494
|
);
|
|
370
495
|
}
|
|
371
496
|
```
|
|
372
497
|
|
|
373
|
-
|
|
498
|
+
**Available methods:**
|
|
499
|
+
|
|
500
|
+
- `request(args)` - EIP-1193 requests
|
|
501
|
+
- `signPersonalMessage(message, address)` - Sign personal message
|
|
502
|
+
- `signTypedData(typedData)` - Sign EIP-712 typed data
|
|
503
|
+
- `sendTransaction(transaction)` - Send transaction
|
|
504
|
+
- `switchChain(chainId)` - Switch chains
|
|
505
|
+
- `getChainId()` - Get current chain ID
|
|
506
|
+
- `getAccounts()` - Get connected accounts
|
|
507
|
+
- `isConnected` - Connection status
|
|
508
|
+
- `isAvailable` - Provider availability
|
|
509
|
+
|
|
510
|
+
### Auto-Confirm Hook (Injected Provider Only)
|
|
511
|
+
|
|
512
|
+
#### useAutoConfirm
|
|
513
|
+
|
|
514
|
+
Hook for managing auto-confirm functionality with the Phantom extension. Auto-confirm allows transactions to be automatically approved without user interaction for enabled chains.
|
|
515
|
+
|
|
516
|
+
> **Note**: This hook only works with the `injected` provider type (Phantom browser extension). It will throw errors for embedded providers.
|
|
374
517
|
|
|
375
518
|
```tsx
|
|
376
|
-
import {
|
|
377
|
-
import { parseEther, parseGwei } from "viem";
|
|
519
|
+
import { useAutoConfirm, NetworkId } from "@phantom/react-sdk";
|
|
378
520
|
|
|
379
|
-
function
|
|
380
|
-
const {
|
|
521
|
+
function AutoConfirmControls() {
|
|
522
|
+
const { enable, disable, status, supportedChains, isLoading, error, refetch } = useAutoConfirm();
|
|
381
523
|
|
|
382
|
-
const
|
|
524
|
+
const handleEnable = async () => {
|
|
383
525
|
try {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E",
|
|
388
|
-
value: parseEther("0.001"), // 0.001 ETH
|
|
389
|
-
gas: 21000n,
|
|
390
|
-
gasPrice: parseGwei("20"), // 20 gwei
|
|
391
|
-
},
|
|
526
|
+
// Enable auto-confirm for specific chains
|
|
527
|
+
const result = await enable({
|
|
528
|
+
chains: [NetworkId.SOLANA_DEVNET, NetworkId.ETHEREUM_MAINNET],
|
|
392
529
|
});
|
|
393
|
-
console.log("
|
|
530
|
+
console.log("Auto-confirm enabled:", result);
|
|
394
531
|
} catch (err) {
|
|
395
|
-
console.error("Failed to
|
|
532
|
+
console.error("Failed to enable auto-confirm:", err);
|
|
396
533
|
}
|
|
397
534
|
};
|
|
398
535
|
|
|
536
|
+
const handleDisable = async () => {
|
|
537
|
+
try {
|
|
538
|
+
await disable();
|
|
539
|
+
console.log("Auto-confirm disabled");
|
|
540
|
+
} catch (err) {
|
|
541
|
+
console.error("Failed to disable auto-confirm:", err);
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
if (isLoading) {
|
|
546
|
+
return <div>Loading auto-confirm settings...</div>;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if (error) {
|
|
550
|
+
return <div>Error: {error.message}</div>;
|
|
551
|
+
}
|
|
552
|
+
|
|
399
553
|
return (
|
|
400
|
-
<
|
|
401
|
-
|
|
402
|
-
|
|
554
|
+
<div>
|
|
555
|
+
<h3>Auto-Confirm Settings</h3>
|
|
556
|
+
|
|
557
|
+
<div>
|
|
558
|
+
<strong>Status:</strong> {status?.enabled ? "Enabled" : "Disabled"}
|
|
559
|
+
{status?.chains && (
|
|
560
|
+
<div>
|
|
561
|
+
<strong>Active Chains:</strong>
|
|
562
|
+
<ul>
|
|
563
|
+
{status.chains.map(chain => (
|
|
564
|
+
<li key={chain}>{chain}</li>
|
|
565
|
+
))}
|
|
566
|
+
</ul>
|
|
567
|
+
</div>
|
|
568
|
+
)}
|
|
569
|
+
</div>
|
|
570
|
+
|
|
571
|
+
<div>
|
|
572
|
+
<strong>Supported Chains:</strong>
|
|
573
|
+
{supportedChains?.chains && (
|
|
574
|
+
<ul>
|
|
575
|
+
{supportedChains.chains.map(chain => (
|
|
576
|
+
<li key={chain}>{chain}</li>
|
|
577
|
+
))}
|
|
578
|
+
</ul>
|
|
579
|
+
)}
|
|
580
|
+
</div>
|
|
581
|
+
|
|
582
|
+
<div>
|
|
583
|
+
<button onClick={handleEnable} disabled={isLoading}>
|
|
584
|
+
Enable Auto-Confirm
|
|
585
|
+
</button>
|
|
586
|
+
<button onClick={handleDisable} disabled={isLoading}>
|
|
587
|
+
Disable Auto-Confirm
|
|
588
|
+
</button>
|
|
589
|
+
<button onClick={refetch} disabled={isLoading}>
|
|
590
|
+
Refresh Status
|
|
591
|
+
</button>
|
|
592
|
+
</div>
|
|
593
|
+
</div>
|
|
403
594
|
);
|
|
404
595
|
}
|
|
405
596
|
```
|
|
406
597
|
|
|
407
|
-
|
|
598
|
+
**Hook Interface:**
|
|
408
599
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
#### Ethereum/EVM
|
|
600
|
+
```typescript
|
|
601
|
+
interface UseAutoConfirmResult {
|
|
602
|
+
enable: (params: AutoConfirmEnableParams) => Promise<AutoConfirmResult>;
|
|
603
|
+
disable: () => Promise<void>;
|
|
604
|
+
status: AutoConfirmResult | null;
|
|
605
|
+
supportedChains: AutoConfirmSupportedChainsResult | null;
|
|
606
|
+
isLoading: boolean;
|
|
607
|
+
error: Error | null;
|
|
608
|
+
refetch: () => Promise<void>;
|
|
609
|
+
}
|
|
420
610
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
- `NetworkId.ARBITRUM_ONE`
|
|
425
|
-
- `NetworkId.OPTIMISM_MAINNET`
|
|
426
|
-
- `NetworkId.BASE_MAINNET`
|
|
611
|
+
interface AutoConfirmEnableParams {
|
|
612
|
+
chains?: NetworkId[]; // Optional array of chains to enable
|
|
613
|
+
}
|
|
427
614
|
|
|
428
|
-
|
|
615
|
+
interface AutoConfirmResult {
|
|
616
|
+
enabled: boolean;
|
|
617
|
+
chains: NetworkId[];
|
|
618
|
+
}
|
|
429
619
|
|
|
430
|
-
|
|
431
|
-
|
|
620
|
+
interface AutoConfirmSupportedChainsResult {
|
|
621
|
+
chains: NetworkId[];
|
|
622
|
+
}
|
|
623
|
+
```
|
|
432
624
|
|
|
433
|
-
|
|
625
|
+
**Available Methods:**
|
|
434
626
|
|
|
435
|
-
- `
|
|
436
|
-
- `
|
|
437
|
-
- `
|
|
627
|
+
- `enable(params)` - Enable auto-confirm for specific chains
|
|
628
|
+
- `disable()` - Disable auto-confirm completely
|
|
629
|
+
- `refetch()` - Refresh status and supported chains from extension
|
|
630
|
+
- `status` - Current auto-confirm status (enabled/disabled and active chains)
|
|
631
|
+
- `supportedChains` - List of chains that support auto-confirm
|
|
632
|
+
- `isLoading` - Loading state for operations
|
|
633
|
+
- `error` - Any errors from auto-confirm operations
|
|
438
634
|
|
|
439
|
-
|
|
635
|
+
**Usage Notes:**
|
|
440
636
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
637
|
+
- Auto-confirm automatically fetches status and supported chains when the hook initializes
|
|
638
|
+
- Only works with injected provider (Phantom extension)
|
|
639
|
+
- Throws errors for embedded providers
|
|
640
|
+
- Status is automatically updated after enable/disable operations
|
|
641
|
+
- Use `refetch()` to manually refresh data from the extension
|
|
445
642
|
|
|
446
643
|
## Transaction Examples
|
|
447
644
|
|
|
@@ -449,10 +646,10 @@ The SDK automatically determines the transaction type from the NetworkId:
|
|
|
449
646
|
|
|
450
647
|
```tsx
|
|
451
648
|
import { VersionedTransaction, TransactionMessage, SystemProgram, PublicKey, Connection } from "@solana/web3.js";
|
|
452
|
-
import {
|
|
649
|
+
import { useSolana } from "@phantom/react-sdk";
|
|
453
650
|
|
|
454
651
|
function SolanaExample() {
|
|
455
|
-
const
|
|
652
|
+
const solana = useSolana();
|
|
456
653
|
|
|
457
654
|
const sendTransaction = async () => {
|
|
458
655
|
// Get recent blockhash
|
|
@@ -460,6 +657,7 @@ function SolanaExample() {
|
|
|
460
657
|
const { blockhash } = await connection.getLatestBlockhash();
|
|
461
658
|
|
|
462
659
|
// Create transfer instruction
|
|
660
|
+
const fromAddress = await solana.getPublicKey();
|
|
463
661
|
const transferInstruction = SystemProgram.transfer({
|
|
464
662
|
fromPubkey: new PublicKey(fromAddress),
|
|
465
663
|
toPubkey: new PublicKey(toAddress),
|
|
@@ -475,12 +673,12 @@ function SolanaExample() {
|
|
|
475
673
|
|
|
476
674
|
const transaction = new VersionedTransaction(messageV0);
|
|
477
675
|
|
|
478
|
-
//
|
|
479
|
-
const result = await signAndSendTransaction(
|
|
480
|
-
|
|
481
|
-
transaction,
|
|
482
|
-
});
|
|
676
|
+
// Sign and send using chain-specific hook
|
|
677
|
+
const result = await solana.signAndSendTransaction(transaction);
|
|
678
|
+
console.log("Transaction sent:", result.hash);
|
|
483
679
|
};
|
|
680
|
+
|
|
681
|
+
return <button onClick={sendTransaction}>Send SOL</button>;
|
|
484
682
|
}
|
|
485
683
|
```
|
|
486
684
|
|
|
@@ -496,15 +694,16 @@ import {
|
|
|
496
694
|
address,
|
|
497
695
|
compileTransaction,
|
|
498
696
|
} from "@solana/kit";
|
|
499
|
-
import {
|
|
697
|
+
import { useSolana } from "@phantom/react-sdk";
|
|
500
698
|
|
|
501
699
|
function SolanaKitExample() {
|
|
502
|
-
const
|
|
700
|
+
const solana = useSolana();
|
|
503
701
|
|
|
504
702
|
const sendTransaction = async () => {
|
|
505
703
|
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
|
|
506
704
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
|
|
507
705
|
|
|
706
|
+
const userPublicKey = await solana.getPublicKey();
|
|
508
707
|
const transactionMessage = pipe(
|
|
509
708
|
createTransactionMessage({ version: 0 }),
|
|
510
709
|
tx => setTransactionMessageFeePayer(address(userPublicKey), tx),
|
|
@@ -513,11 +712,12 @@ function SolanaKitExample() {
|
|
|
513
712
|
|
|
514
713
|
const transaction = compileTransaction(transactionMessage);
|
|
515
714
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
});
|
|
715
|
+
// Sign and send using chain-specific hook
|
|
716
|
+
const result = await solana.signAndSendTransaction(transaction);
|
|
717
|
+
console.log("Transaction sent:", result.hash);
|
|
520
718
|
};
|
|
719
|
+
|
|
720
|
+
return <button onClick={sendTransaction}>Send SOL</button>;
|
|
521
721
|
}
|
|
522
722
|
```
|
|
523
723
|
|
|
@@ -525,96 +725,40 @@ function SolanaKitExample() {
|
|
|
525
725
|
|
|
526
726
|
```tsx
|
|
527
727
|
import { parseEther, parseGwei, encodeFunctionData } from "viem";
|
|
528
|
-
import {
|
|
728
|
+
import { useEthereum } from "@phantom/react-sdk";
|
|
529
729
|
|
|
530
730
|
function EthereumExample() {
|
|
531
|
-
const
|
|
731
|
+
const ethereum = useEthereum();
|
|
532
732
|
|
|
533
733
|
const sendEth = async () => {
|
|
534
|
-
const result = await
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
gas: 21000n,
|
|
540
|
-
gasPrice: parseGwei("20"), // 20 gwei
|
|
541
|
-
},
|
|
734
|
+
const result = await ethereum.sendTransaction({
|
|
735
|
+
to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E",
|
|
736
|
+
value: parseEther("1").toString(), // 1 ETH
|
|
737
|
+
gas: "21000",
|
|
738
|
+
gasPrice: parseGwei("20").toString(), // 20 gwei
|
|
542
739
|
});
|
|
740
|
+
console.log("ETH sent:", result.hash);
|
|
543
741
|
};
|
|
544
742
|
|
|
545
743
|
const sendToken = async () => {
|
|
546
|
-
const result = await
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
maxFeePerGas: parseGwei("30"),
|
|
557
|
-
maxPriorityFeePerGas: parseGwei("2"),
|
|
558
|
-
},
|
|
559
|
-
});
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
## Advanced Usage
|
|
565
|
-
|
|
566
|
-
### Multi-Chain Application
|
|
567
|
-
|
|
568
|
-
```tsx
|
|
569
|
-
import { useSignAndSendTransaction, NetworkId } from "@phantom/react-sdk";
|
|
570
|
-
import { VersionedTransaction, TransactionMessage, SystemProgram, PublicKey, Connection } from "@solana/web3.js";
|
|
571
|
-
import { parseEther } from "viem";
|
|
572
|
-
|
|
573
|
-
function MultiChainWallet() {
|
|
574
|
-
const { signAndSendTransaction } = useSignAndSendTransaction();
|
|
575
|
-
|
|
576
|
-
const sendSolana = async () => {
|
|
577
|
-
// Get recent blockhash
|
|
578
|
-
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
579
|
-
const { blockhash } = await connection.getLatestBlockhash();
|
|
580
|
-
|
|
581
|
-
// Create transfer instruction
|
|
582
|
-
const transferInstruction = SystemProgram.transfer({
|
|
583
|
-
fromPubkey: new PublicKey(solanaAddress),
|
|
584
|
-
toPubkey: new PublicKey(recipient),
|
|
585
|
-
lamports: 1000000,
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
// Create VersionedTransaction
|
|
589
|
-
const messageV0 = new TransactionMessage({
|
|
590
|
-
payerKey: new PublicKey(solanaAddress),
|
|
591
|
-
recentBlockhash: blockhash,
|
|
592
|
-
instructions: [transferInstruction],
|
|
593
|
-
}).compileToV0Message();
|
|
594
|
-
|
|
595
|
-
const transaction = new VersionedTransaction(messageV0);
|
|
596
|
-
|
|
597
|
-
return await signAndSendTransaction({
|
|
598
|
-
networkId: NetworkId.SOLANA_MAINNET,
|
|
599
|
-
transaction,
|
|
600
|
-
});
|
|
601
|
-
};
|
|
602
|
-
|
|
603
|
-
const sendEthereum = async () => {
|
|
604
|
-
return await signAndSendTransaction({
|
|
605
|
-
networkId: NetworkId.ETHEREUM_MAINNET,
|
|
606
|
-
transaction: {
|
|
607
|
-
to: recipient,
|
|
608
|
-
value: parseEther("0.1"),
|
|
609
|
-
gas: 21000n,
|
|
610
|
-
},
|
|
744
|
+
const result = await ethereum.sendTransaction({
|
|
745
|
+
to: tokenContractAddress,
|
|
746
|
+
data: encodeFunctionData({
|
|
747
|
+
abi: erc20Abi,
|
|
748
|
+
functionName: "transfer",
|
|
749
|
+
args: [recipientAddress, parseEther("100")],
|
|
750
|
+
}),
|
|
751
|
+
gas: "50000",
|
|
752
|
+
maxFeePerGas: parseGwei("30").toString(),
|
|
753
|
+
maxPriorityFeePerGas: parseGwei("2").toString(),
|
|
611
754
|
});
|
|
755
|
+
console.log("Token sent:", result.hash);
|
|
612
756
|
};
|
|
613
757
|
|
|
614
758
|
return (
|
|
615
759
|
<div>
|
|
616
|
-
<button onClick={
|
|
617
|
-
<button onClick={
|
|
760
|
+
<button onClick={sendEth}>Send ETH</button>
|
|
761
|
+
<button onClick={sendToken}>Send Token</button>
|
|
618
762
|
</div>
|
|
619
763
|
);
|
|
620
764
|
}
|
|
@@ -624,28 +768,27 @@ function MultiChainWallet() {
|
|
|
624
768
|
|
|
625
769
|
Quick reference of all available hooks:
|
|
626
770
|
|
|
627
|
-
| Hook
|
|
628
|
-
|
|
|
629
|
-
| `useConnect`
|
|
630
|
-
| `useAccounts`
|
|
631
|
-
| `useIsExtensionInstalled`
|
|
632
|
-
| `useDisconnect`
|
|
633
|
-
| `
|
|
634
|
-
| `
|
|
635
|
-
| `
|
|
771
|
+
| Hook | Purpose | Returns |
|
|
772
|
+
| ------------------------- | --------------------------------------- | --------------------------------------------------- |
|
|
773
|
+
| `useConnect` | Connect to wallet | `{ connect, isConnecting, error }` |
|
|
774
|
+
| `useAccounts` | Get wallet addresses | `WalletAddress[]` or `null` |
|
|
775
|
+
| `useIsExtensionInstalled` | Check extension status | `{ isLoading, isInstalled }` |
|
|
776
|
+
| `useDisconnect` | Disconnect from wallet | `{ disconnect, isDisconnecting }` |
|
|
777
|
+
| `useAutoConfirm` | Auto-confirm management (injected only) | `{ enable, disable, status, supportedChains, ... }` |
|
|
778
|
+
| `useSolana` | Solana chain operations | `{ signMessage, signAndSendTransaction, ... }` |
|
|
779
|
+
| `useEthereum` | Ethereum chain operations | `{ signPersonalMessage, sendTransaction, ... }` |
|
|
780
|
+
| `usePhantom` | Get provider context | `{ isConnected, isReady }` |
|
|
636
781
|
|
|
637
782
|
## Configuration Reference
|
|
638
783
|
|
|
639
784
|
```typescript
|
|
640
785
|
interface PhantomSDKConfig {
|
|
641
786
|
providerType: "injected" | "embedded";
|
|
642
|
-
appName?: string; // Optional app name for branding
|
|
643
|
-
appLogo?: string; // Optional app logo URL for branding
|
|
644
787
|
addressTypes?: [AddressType, ...AddressType[]]; // Networks to enable (e.g., [AddressType.solana])
|
|
645
788
|
|
|
646
789
|
// Required for embedded provider only
|
|
647
790
|
apiBaseUrl?: string; // Phantom API base URL
|
|
648
|
-
|
|
791
|
+
appId: string; // Your app ID
|
|
649
792
|
authOptions?: {
|
|
650
793
|
authUrl?: string; // Custom auth URL (optional)
|
|
651
794
|
redirectUrl?: string; // Custom redirect URL (optional)
|
|
@@ -664,8 +807,8 @@ The React SDK supports separate debug configuration that can be changed without
|
|
|
664
807
|
|
|
665
808
|
```typescript
|
|
666
809
|
interface PhantomDebugConfig {
|
|
667
|
-
enabled?: boolean;
|
|
668
|
-
level?: DebugLevel;
|
|
810
|
+
enabled?: boolean; // Enable debug logging
|
|
811
|
+
level?: DebugLevel; // Debug level (ERROR, WARN, INFO, DEBUG)
|
|
669
812
|
callback?: DebugCallback; // Custom debug message handler
|
|
670
813
|
}
|
|
671
814
|
```
|
|
@@ -684,7 +827,6 @@ function App() {
|
|
|
684
827
|
// SDK configuration - static, won't change when debug settings change
|
|
685
828
|
const config: PhantomSDKConfig = {
|
|
686
829
|
providerType: "embedded",
|
|
687
|
-
organizationId: "your-org-id",
|
|
688
830
|
// ... other config
|
|
689
831
|
};
|
|
690
832
|
|
|
@@ -711,12 +853,12 @@ Debug callbacks receive a `DebugMessage` object:
|
|
|
711
853
|
|
|
712
854
|
```typescript
|
|
713
855
|
interface DebugMessage {
|
|
714
|
-
timestamp: number;
|
|
715
|
-
level: DebugLevel;
|
|
716
|
-
category: string;
|
|
717
|
-
message: string;
|
|
718
|
-
data?: any;
|
|
856
|
+
timestamp: number; // Unix timestamp
|
|
857
|
+
level: DebugLevel; // Message level
|
|
858
|
+
category: string; // Component category
|
|
859
|
+
message: string; // Debug message text
|
|
860
|
+
data?: any; // Additional debug data (optional)
|
|
719
861
|
}
|
|
720
862
|
```
|
|
721
863
|
|
|
722
|
-
For more details
|
|
864
|
+
For more details and examples, see the [@phantom/browser-sdk documentation](../browser-sdk/README.md).
|