@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/dist/index.mjs
CHANGED
|
@@ -4,14 +4,6 @@ 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 [isConnected, setIsConnected] = useState(false);
|
|
8
|
-
const [isConnecting, setIsConnecting] = useState(false);
|
|
9
|
-
const [connectError, setConnectError] = useState(null);
|
|
10
|
-
const [addresses, setAddresses] = useState([]);
|
|
11
|
-
const [walletId, setWalletId] = useState(null);
|
|
12
|
-
const [currentProviderType, setCurrentProviderType] = useState(config.providerType || null);
|
|
13
|
-
const [isPhantomAvailable, setIsPhantomAvailable] = useState(false);
|
|
14
|
-
const [sdk, setSdk] = useState(null);
|
|
15
7
|
const memoizedConfig = useMemo(() => {
|
|
16
8
|
return {
|
|
17
9
|
...config,
|
|
@@ -19,6 +11,16 @@ function PhantomProvider({ children, config, debugConfig }) {
|
|
|
19
11
|
providerType: config.providerType || "embedded"
|
|
20
12
|
};
|
|
21
13
|
}, [config]);
|
|
14
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
15
|
+
const [isConnecting, setIsConnecting] = useState(false);
|
|
16
|
+
const [connectError, setConnectError] = useState(null);
|
|
17
|
+
const [addresses, setAddresses] = useState([]);
|
|
18
|
+
const [walletId, setWalletId] = useState(null);
|
|
19
|
+
const [currentProviderType, setCurrentProviderType] = useState(
|
|
20
|
+
memoizedConfig.providerType || null
|
|
21
|
+
);
|
|
22
|
+
const [isPhantomAvailable, setIsPhantomAvailable] = useState(false);
|
|
23
|
+
const [sdk, setSdk] = useState(null);
|
|
22
24
|
useEffect(() => {
|
|
23
25
|
const sdkInstance = new BrowserSDK(memoizedConfig);
|
|
24
26
|
const handleConnectStart = () => {
|
|
@@ -77,19 +79,19 @@ function PhantomProvider({ children, config, debugConfig }) {
|
|
|
77
79
|
return;
|
|
78
80
|
const initialize = async () => {
|
|
79
81
|
try {
|
|
80
|
-
const available = await
|
|
82
|
+
const available = await BrowserSDK.isPhantomInstalled();
|
|
81
83
|
setIsPhantomAvailable(available);
|
|
82
84
|
} catch (err) {
|
|
83
85
|
console.error("Error checking Phantom extension:", err);
|
|
84
86
|
setIsPhantomAvailable(false);
|
|
85
87
|
}
|
|
86
|
-
if (
|
|
88
|
+
if (memoizedConfig.autoConnect !== false) {
|
|
87
89
|
sdk.autoConnect().catch(() => {
|
|
88
90
|
});
|
|
89
91
|
}
|
|
90
92
|
};
|
|
91
93
|
initialize();
|
|
92
|
-
}, [sdk,
|
|
94
|
+
}, [sdk, memoizedConfig.autoConnect]);
|
|
93
95
|
const value = useMemo(
|
|
94
96
|
() => ({
|
|
95
97
|
sdk,
|
|
@@ -101,16 +103,7 @@ function PhantomProvider({ children, config, debugConfig }) {
|
|
|
101
103
|
currentProviderType,
|
|
102
104
|
isPhantomAvailable
|
|
103
105
|
}),
|
|
104
|
-
[
|
|
105
|
-
sdk,
|
|
106
|
-
isConnected,
|
|
107
|
-
isConnecting,
|
|
108
|
-
connectError,
|
|
109
|
-
addresses,
|
|
110
|
-
walletId,
|
|
111
|
-
currentProviderType,
|
|
112
|
-
isPhantomAvailable
|
|
113
|
-
]
|
|
106
|
+
[sdk, isConnected, isConnecting, connectError, addresses, walletId, currentProviderType, isPhantomAvailable]
|
|
114
107
|
);
|
|
115
108
|
return /* @__PURE__ */ jsx(PhantomContext.Provider, { value, children });
|
|
116
109
|
}
|
|
@@ -178,115 +171,311 @@ function useDisconnect() {
|
|
|
178
171
|
};
|
|
179
172
|
}
|
|
180
173
|
|
|
181
|
-
// src/hooks/
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
setError(null);
|
|
174
|
+
// src/hooks/useAccounts.ts
|
|
175
|
+
function useAccounts() {
|
|
176
|
+
const { addresses, isConnected } = usePhantom();
|
|
177
|
+
return isConnected ? addresses : null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/hooks/useIsExtensionInstalled.ts
|
|
181
|
+
import * as React from "react";
|
|
182
|
+
import { waitForPhantomExtension } from "@phantom/browser-sdk";
|
|
183
|
+
function useIsExtensionInstalled() {
|
|
184
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
185
|
+
const [isInstalled, setIsInstalled] = React.useState(false);
|
|
186
|
+
React.useEffect(() => {
|
|
187
|
+
let isMounted = true;
|
|
188
|
+
const checkExtension = async () => {
|
|
197
189
|
try {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
190
|
+
setIsLoading(true);
|
|
191
|
+
const result = await waitForPhantomExtension(3e3);
|
|
192
|
+
if (isMounted) {
|
|
193
|
+
setIsInstalled(result);
|
|
194
|
+
}
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (isMounted) {
|
|
197
|
+
setIsInstalled(false);
|
|
198
|
+
}
|
|
203
199
|
} finally {
|
|
204
|
-
|
|
200
|
+
if (isMounted) {
|
|
201
|
+
setIsLoading(false);
|
|
202
|
+
}
|
|
205
203
|
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
};
|
|
204
|
+
};
|
|
205
|
+
checkExtension();
|
|
206
|
+
return () => {
|
|
207
|
+
isMounted = false;
|
|
208
|
+
};
|
|
209
|
+
}, []);
|
|
210
|
+
return { isLoading, isInstalled };
|
|
214
211
|
}
|
|
215
212
|
|
|
216
|
-
// src/hooks/
|
|
217
|
-
import { useCallback as
|
|
218
|
-
function
|
|
219
|
-
const { sdk } = usePhantom();
|
|
220
|
-
const [
|
|
213
|
+
// src/hooks/useAutoConfirm.ts
|
|
214
|
+
import { useCallback as useCallback3, useState as useState4, useEffect as useEffect3 } from "react";
|
|
215
|
+
function useAutoConfirm() {
|
|
216
|
+
const { sdk, currentProviderType } = usePhantom();
|
|
217
|
+
const [status, setStatus] = useState4(null);
|
|
218
|
+
const [supportedChains, setSupportedChains] = useState4(null);
|
|
219
|
+
const [isLoading, setIsLoading] = useState4(false);
|
|
221
220
|
const [error, setError] = useState4(null);
|
|
222
|
-
const
|
|
221
|
+
const isInjected = currentProviderType === "injected";
|
|
222
|
+
const enable = useCallback3(
|
|
223
223
|
async (params) => {
|
|
224
224
|
if (!sdk) {
|
|
225
225
|
throw new Error("SDK not initialized");
|
|
226
226
|
}
|
|
227
|
-
if (!
|
|
228
|
-
throw new Error("
|
|
227
|
+
if (!isInjected) {
|
|
228
|
+
throw new Error("Auto-confirm is only available for injected (extension) providers");
|
|
229
229
|
}
|
|
230
|
-
setIsSigning(true);
|
|
231
|
-
setError(null);
|
|
232
230
|
try {
|
|
233
|
-
|
|
231
|
+
setIsLoading(true);
|
|
232
|
+
setError(null);
|
|
233
|
+
const result = await sdk.enableAutoConfirm(params);
|
|
234
|
+
setStatus(result);
|
|
234
235
|
return result;
|
|
235
236
|
} catch (err) {
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
const error2 = err instanceof Error ? err : new Error("Unknown error occurred");
|
|
238
|
+
setError(error2);
|
|
239
|
+
throw error2;
|
|
238
240
|
} finally {
|
|
239
|
-
|
|
241
|
+
setIsLoading(false);
|
|
240
242
|
}
|
|
241
243
|
},
|
|
242
|
-
[sdk]
|
|
244
|
+
[sdk, isInjected]
|
|
243
245
|
);
|
|
246
|
+
const disable = useCallback3(async () => {
|
|
247
|
+
if (!sdk) {
|
|
248
|
+
throw new Error("SDK not initialized");
|
|
249
|
+
}
|
|
250
|
+
if (!isInjected) {
|
|
251
|
+
throw new Error("Auto-confirm is only available for injected (extension) providers");
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
setIsLoading(true);
|
|
255
|
+
setError(null);
|
|
256
|
+
await sdk.disableAutoConfirm();
|
|
257
|
+
const newStatus = await sdk.getAutoConfirmStatus();
|
|
258
|
+
setStatus(newStatus);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
const error2 = err instanceof Error ? err : new Error("Unknown error occurred");
|
|
261
|
+
setError(error2);
|
|
262
|
+
throw error2;
|
|
263
|
+
} finally {
|
|
264
|
+
setIsLoading(false);
|
|
265
|
+
}
|
|
266
|
+
}, [sdk, isInjected]);
|
|
267
|
+
const refetch = useCallback3(async () => {
|
|
268
|
+
if (!sdk || !isInjected) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
setIsLoading(true);
|
|
273
|
+
setError(null);
|
|
274
|
+
const [statusResult, supportedResult] = await Promise.all([
|
|
275
|
+
sdk.getAutoConfirmStatus(),
|
|
276
|
+
sdk.getSupportedAutoConfirmChains()
|
|
277
|
+
]);
|
|
278
|
+
setStatus(statusResult);
|
|
279
|
+
setSupportedChains(supportedResult);
|
|
280
|
+
} catch (err) {
|
|
281
|
+
const error2 = err instanceof Error ? err : new Error("Failed to fetch auto-confirm data");
|
|
282
|
+
setError(error2);
|
|
283
|
+
} finally {
|
|
284
|
+
setIsLoading(false);
|
|
285
|
+
}
|
|
286
|
+
}, [sdk, isInjected]);
|
|
287
|
+
useEffect3(() => {
|
|
288
|
+
if (sdk && isInjected) {
|
|
289
|
+
refetch();
|
|
290
|
+
} else {
|
|
291
|
+
setStatus(null);
|
|
292
|
+
setSupportedChains(null);
|
|
293
|
+
setError(null);
|
|
294
|
+
}
|
|
295
|
+
}, [sdk, isInjected, refetch]);
|
|
244
296
|
return {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
297
|
+
enable,
|
|
298
|
+
disable,
|
|
299
|
+
status,
|
|
300
|
+
supportedChains,
|
|
301
|
+
isLoading,
|
|
302
|
+
error,
|
|
303
|
+
refetch
|
|
248
304
|
};
|
|
249
305
|
}
|
|
250
306
|
|
|
251
|
-
// src/hooks/
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
307
|
+
// src/hooks/useSolana.ts
|
|
308
|
+
import { useCallback as useCallback4, useMemo as useMemo2 } from "react";
|
|
309
|
+
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]);
|
|
370
|
+
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,
|
|
381
|
+
// State
|
|
382
|
+
isAvailable: !!solanaChain,
|
|
383
|
+
isConnected: solanaChain?.isConnected() ?? false
|
|
384
|
+
};
|
|
255
385
|
}
|
|
256
386
|
|
|
257
|
-
// src/hooks/
|
|
258
|
-
import
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
if (cachedIsInstalled !== null) {
|
|
270
|
-
setIsInstalled(cachedIsInstalled);
|
|
271
|
-
setIsLoading(false);
|
|
272
|
-
return;
|
|
387
|
+
// src/hooks/useEthereum.ts
|
|
388
|
+
import { useCallback as useCallback5, useMemo as useMemo3 } from "react";
|
|
389
|
+
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;
|
|
273
398
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
+
);
|
|
462
|
+
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,
|
|
475
|
+
// State
|
|
476
|
+
isAvailable: !!ethereumChain,
|
|
477
|
+
isConnected: ethereumChain?.isConnected() ?? false
|
|
478
|
+
};
|
|
290
479
|
}
|
|
291
480
|
|
|
292
481
|
// src/index.ts
|
|
@@ -298,10 +487,11 @@ export {
|
|
|
298
487
|
PhantomProvider,
|
|
299
488
|
debug,
|
|
300
489
|
useAccounts,
|
|
490
|
+
useAutoConfirm,
|
|
301
491
|
useConnect,
|
|
302
492
|
useDisconnect,
|
|
493
|
+
useEthereum,
|
|
303
494
|
useIsExtensionInstalled,
|
|
304
495
|
usePhantom,
|
|
305
|
-
|
|
306
|
-
useSignMessage
|
|
496
|
+
useSolana
|
|
307
497
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phantom/react-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/index.mjs",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
"prettier": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@phantom/browser-sdk": "^0.
|
|
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"
|
|
30
32
|
},
|
|
31
33
|
"devDependencies": {
|
|
32
34
|
"@testing-library/dom": "^10.4.0",
|