@solana/react 5.3.0 → 5.4.0-canary-20260108180834

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
@@ -306,3 +306,179 @@ function SignAndSendTransactionButton({ account, transactionBytes }) {
306
306
  );
307
307
  }
308
308
  ```
309
+
310
+ ### `useSignTransactions(uiWalletAccount, chain)`
311
+
312
+ Given a `UiWalletAccount` and a chain that begins with `solana:`, this hook returns a function you can call to sign one or more serialized transactions in a single request.
313
+
314
+ #### Arguments
315
+
316
+ One or more config objects with the following properties:
317
+
318
+ - `options`: An object with the following properties:
319
+ - `minContextSlot`: A slot at which any blockhash/nonce in the transaction is known to exist. This may be used by the signer and/or RPC to determine the validity of the blockhashes/nonces it has observed.
320
+ - `transaction`: A `Uint8Array` of bytes that conforms to the [Solana transaction schema](https://solana.com/docs/core/transactions#transaction)
321
+
322
+ #### Returns
323
+
324
+ An array of objects with the following properties:
325
+
326
+ - `signedTransaction`: A `Uint8Array` of bytes that conforms to the [Solana transaction schema](https://solana.com/docs/core/transactions#transaction)
327
+
328
+ #### Example
329
+
330
+ ```tsx
331
+ import { useSignTransactions } from '@solana/react';
332
+
333
+ function SignTransactionsButton({ account, transactionBytes1, transactionBytes2 }) {
334
+ const signTransactions = useSignTransactions(account, 'solana:devnet');
335
+ return (
336
+ <button
337
+ onClick={async () => {
338
+ try {
339
+ const [{ signedTransaction: first }, { signedTransaction: second }] = await signTransactions(
340
+ { transaction: transactionBytes1 },
341
+ { transaction: transactionBytes2 },
342
+ );
343
+ window.alert(`Signed transaction bytes: ${first.toString()} and ${second.toString()}`);
344
+ } catch (e) {
345
+ console.error('Failed to sign transactions', e);
346
+ }
347
+ }}
348
+ >
349
+ Sign Transactions
350
+ </button>
351
+ );
352
+ }
353
+ ```
354
+
355
+ ### `useSignAndSendTransactions(uiWalletAccount, chain)`
356
+
357
+ Given a `UiWalletAccount` and a chain that begins with `solana:`, this hook returns a function you can call to sign and send one or more serialized transactions in a single request.
358
+
359
+ #### Arguments
360
+
361
+ One or more config objects with the following properties:
362
+
363
+ - `options`: An object with the following properties:
364
+ - `minContextSlot`: A slot at which any blockhash/nonce in the transaction is known to exist. This may be used by the signer and/or RPC to determine the validity of the blockhashes/nonces it has observed.
365
+ - `transaction`: A `Uint8Array` of bytes that conforms to the [Solana transaction schema](https://solana.com/docs/core/transactions#transaction)
366
+
367
+ #### Returns
368
+
369
+ An array of objects with the following properties:
370
+
371
+ - `signature`: A `Uint8Array` of bytes representing the signature of each sent transaction.
372
+
373
+ #### Example
374
+
375
+ ```tsx
376
+ import { getBase58Decoder } from '@solana/codecs-strings';
377
+ import { useSignAndSendTransactions } from '@solana/react';
378
+
379
+ function SignAndSendTransactionsButton({ account, transactionBytes1, transactionBytes2 }) {
380
+ const signAndSendTransactions = useSignAndSendTransactions(account, 'solana:devnet');
381
+ return (
382
+ <button
383
+ onClick={async () => {
384
+ try {
385
+ const [first, second] = await signAndSendTransactions(
386
+ { transaction: transactionBytes1 },
387
+ { transaction: transactionBytes2 },
388
+ );
389
+ const [firstSignature, secondSignature] = [first.signature, second.signature].map(signature =>
390
+ getBase58Decoder().decode(signature),
391
+ );
392
+ window.alert(
393
+ `View transactions: https://explorer.solana.com/tx/${firstSignature}?cluster=devnet and https://explorer.solana.com/tx/${secondSignature}?cluster=devnet`,
394
+ );
395
+ } catch (e) {
396
+ console.error('Error returned by signAndSendTransactions', e);
397
+ }
398
+ }}
399
+ >
400
+ Sign and Send Transactions
401
+ </button>
402
+ );
403
+ }
404
+ ```
405
+
406
+ ### `useSelectedWalletAccount()`
407
+
408
+ This hook returns the wallet account that is selected, a function to change the selection, and a list of wallets which pass a filter condition you have provided. This hook must be used in a React Component inside `SelectedWalletAccountContextProvider`.
409
+
410
+ #### Arguments
411
+
412
+ This hook doesn't take any arguments.
413
+
414
+ #### Returns
415
+
416
+ The function returns an array consisting of the following elements in the order given:
417
+
418
+ - `SelectedWalletAccount`: This element could be a `UiWalletAccount` or `undefined`, and represents the selected wallet account.
419
+ - `SetSelectedWalletAccount`: A setter function to set the SelectedWalletAccount state. It takes an argument which could be a callback function `(prevState)=>newState` or `newState`.
420
+ - `filteredWallets`: List of filtered wallets using the function provided as `filterWallet` function in `SelectedWalletAccountContextProvider`
421
+
422
+ #### Example
423
+
424
+ ```tsx
425
+ import React from 'react';
426
+ import { useSelectedWalletAccount } from '@solana/react';
427
+
428
+ function WalletInfo() {
429
+ const [selectedAccount, setSelectedAccount, filteredWallets] = useSelectedWalletAccount();
430
+
431
+ if (!selectedAccount) {
432
+ return <div>No wallet selected</div>;
433
+ }
434
+
435
+ return (
436
+ <div>
437
+ <p>Address: {selectedAccount.address}</p>
438
+
439
+ <button onClick={() => setSelectedAccount(undefined)}>Clear selection</button>
440
+
441
+ <p>Available wallets: {filteredWallets.length}</p>
442
+ </div>
443
+ );
444
+ }
445
+ ```
446
+
447
+ ### `SelectedWalletAccountContextProvider`
448
+
449
+ This is a react context provider for `SelectedWalletAccountContext`. It provides its children access to the context by using either `useSelectedWalletAccount()` or `useContext(SelectedWalletAccountContext)`.
450
+
451
+ #### Props
452
+
453
+ The provider takes the following props:
454
+
455
+ - `filterWallet`: a function used to filter supported wallets. For example you might use this to restrict your app to wallets that support `solana:mainnet`.
456
+ - `stateSync`: an object to store the selected wallet, with these properties:
457
+ - `storeSelectedWallet`: a function used to store a selected wallet account identifier (as a string) into persistent storage. For example this might write to local storage in the browser. The string stored is `${walletName}:${accountAddress}`.
458
+ - `getSelectedWallet`: a function used to retrieve the persisted wallet account identifier from the persistent storage.
459
+ - `deleteSelectedWallet`: clears any persisted wallet account identifier from the persistent storage.
460
+
461
+ #### Example
462
+
463
+ ```tsx
464
+ import React from 'react';
465
+ import { SelectedWalletAccountContextProvider } from '@solana/react';
466
+ import type { UiWallet } from '@wallet-standard/react';
467
+
468
+ const STORAGE_KEY = 'solana-wallet-account-id';
469
+
470
+ export function App() {
471
+ return (
472
+ <SelectedWalletAccountContextProvider
473
+ filterWallet={(wallet: UiWallet) => wallet.accounts.length > 0}
474
+ stateSync={{
475
+ getSelectedWallet: () => localStorage.getItem(STORAGE_KEY),
476
+ storeSelectedWallet: accountKey => localStorage.setItem(STORAGE_KEY, accountKey),
477
+ deleteSelectedWallet: () => localStorage.removeItem(STORAGE_KEY),
478
+ }}
479
+ >
480
+ <WalletInfo />
481
+ </SelectedWalletAccountContextProvider>
482
+ );
483
+ }
484
+ ```
@@ -4,17 +4,22 @@ var walletStandardFeatures = require('@solana/wallet-standard-features');
4
4
  var errors = require('@wallet-standard/errors');
5
5
  var ui = require('@wallet-standard/ui');
6
6
  var uiRegistry = require('@wallet-standard/ui-registry');
7
- var react = require('react');
7
+ var React2 = require('react');
8
8
  var addresses = require('@solana/addresses');
9
9
  var errors$1 = require('@solana/errors');
10
10
  var promises = require('@solana/promises');
11
11
  var transactionMessages = require('@solana/transaction-messages');
12
12
  var transactions = require('@solana/transactions');
13
+ var react = require('@wallet-standard/react');
14
+
15
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
+
17
+ var React2__default = /*#__PURE__*/_interopDefault(React2);
13
18
 
14
19
  // src/useSignAndSendTransaction.ts
15
20
  function useSignAndSendTransaction(uiWalletAccount, chain) {
16
21
  const signAndSendTransactions = useSignAndSendTransactions(uiWalletAccount, chain);
17
- return react.useCallback(
22
+ return React2.useCallback(
18
23
  async (input) => {
19
24
  const [result] = await signAndSendTransactions(input);
20
25
  return result;
@@ -37,7 +42,7 @@ function useSignAndSendTransactions(uiWalletAccount, chain) {
37
42
  walletStandardFeatures.SolanaSignAndSendTransaction
38
43
  );
39
44
  const account = uiRegistry.getWalletAccountForUiWalletAccount_DO_NOT_USE_OR_YOU_WILL_BE_FIRED(uiWalletAccount);
40
- return react.useCallback(
45
+ return React2.useCallback(
41
46
  async (...inputs) => {
42
47
  const inputsWithChainAndAccount = inputs.map(({ options, ...rest }) => {
43
48
  const minContextSlot = options?.minContextSlot;
@@ -60,7 +65,7 @@ function useSignAndSendTransactions(uiWalletAccount, chain) {
60
65
  }
61
66
  function useSignIn(uiWalletHandle) {
62
67
  const signIns = useSignIns(uiWalletHandle);
63
- return react.useCallback(
68
+ return React2.useCallback(
64
69
  async (input) => {
65
70
  const [result] = await signIns(input);
66
71
  return result;
@@ -80,7 +85,7 @@ function useSignIns(uiWalletHandle) {
80
85
  signMessageFeature = ui.getWalletFeature(uiWalletHandle, walletStandardFeatures.SolanaSignIn);
81
86
  }
82
87
  const wallet = uiRegistry.getWalletForHandle_DO_NOT_USE_OR_YOU_WILL_BE_FIRED(uiWalletHandle);
83
- return react.useCallback(
88
+ return React2.useCallback(
84
89
  async (...inputs) => {
85
90
  const inputsWithAddressAndChainId = inputs.map((input) => ({
86
91
  ...input,
@@ -109,7 +114,7 @@ function useSignIns(uiWalletHandle) {
109
114
  }
110
115
  function useSignMessage(...config) {
111
116
  const signMessages = useSignMessages(...config);
112
- return react.useCallback(
117
+ return React2.useCallback(
113
118
  async (input) => {
114
119
  const [result] = await signMessages(input);
115
120
  return result;
@@ -123,7 +128,7 @@ function useSignMessages(uiWalletAccount) {
123
128
  walletStandardFeatures.SolanaSignMessage
124
129
  );
125
130
  const account = uiRegistry.getWalletAccountForUiWalletAccount_DO_NOT_USE_OR_YOU_WILL_BE_FIRED(uiWalletAccount);
126
- return react.useCallback(
131
+ return React2.useCallback(
127
132
  async (...inputs) => {
128
133
  const inputsWithAccount = inputs.map((input) => ({ ...input, account }));
129
134
  const results = await signMessageFeature.signMessage(...inputsWithAccount);
@@ -141,7 +146,7 @@ function useSignMessages(uiWalletAccount) {
141
146
  }
142
147
  function useSignTransaction(uiWalletAccount, chain) {
143
148
  const signTransactions = useSignTransactions(uiWalletAccount, chain);
144
- return react.useCallback(
149
+ return React2.useCallback(
145
150
  async (input) => {
146
151
  const [result] = await signTransactions(input);
147
152
  return result;
@@ -164,7 +169,7 @@ function useSignTransactions(uiWalletAccount, chain) {
164
169
  walletStandardFeatures.SolanaSignTransaction
165
170
  );
166
171
  const account = uiRegistry.getWalletAccountForUiWalletAccount_DO_NOT_USE_OR_YOU_WILL_BE_FIRED(uiWalletAccount);
167
- return react.useCallback(
172
+ return React2.useCallback(
168
173
  async (...inputs) => {
169
174
  const inputsWithAccountAndChain = inputs.map(({ options, ...rest }) => {
170
175
  const minContextSlot = options?.minContextSlot;
@@ -190,7 +195,7 @@ function bytesEqual(bytes1, bytes2) {
190
195
  }
191
196
  function useWalletAccountMessageSigner(uiWalletAccount) {
192
197
  const signMessage = useSignMessage(uiWalletAccount);
193
- return react.useMemo(
198
+ return React2.useMemo(
194
199
  () => ({
195
200
  address: addresses.address(uiWalletAccount.address),
196
201
  async modifyAndSignMessages(messages, config) {
@@ -226,9 +231,9 @@ function useWalletAccountMessageSigner(uiWalletAccount) {
226
231
  );
227
232
  }
228
233
  function useWalletAccountTransactionSigner(uiWalletAccount, chain) {
229
- const encoderRef = react.useRef(null);
234
+ const encoderRef = React2.useRef(null);
230
235
  const signTransaction = useSignTransaction(uiWalletAccount, chain);
231
- return react.useMemo(
236
+ return React2.useMemo(
232
237
  () => ({
233
238
  address: addresses.address(uiWalletAccount.address),
234
239
  async modifyAndSignTransactions(transactions$1, config = {}) {
@@ -291,9 +296,9 @@ function useWalletAccountTransactionSigner(uiWalletAccount, chain) {
291
296
  );
292
297
  }
293
298
  function useWalletAccountTransactionSendingSigner(uiWalletAccount, chain) {
294
- const encoderRef = react.useRef(null);
299
+ const encoderRef = React2.useRef(null);
295
300
  const signAndSendTransaction = useSignAndSendTransaction(uiWalletAccount, chain);
296
- return react.useMemo(
301
+ return React2.useMemo(
297
302
  () => ({
298
303
  address: addresses.address(uiWalletAccount.address),
299
304
  async signAndSendTransactions(transactions$1, config = {}) {
@@ -319,11 +324,110 @@ function useWalletAccountTransactionSendingSigner(uiWalletAccount, chain) {
319
324
  [signAndSendTransaction, uiWalletAccount.address]
320
325
  );
321
326
  }
327
+ var SelectedWalletAccountContext = /* @__PURE__ */ React2.createContext([
328
+ void 0,
329
+ () => {
330
+ },
331
+ []
332
+ ]);
333
+ function useSelectedWalletAccount() {
334
+ const ctx = React2__default.default.useContext(SelectedWalletAccountContext);
335
+ return ctx;
336
+ }
337
+
338
+ // src/SelectedWalletAccountContextProvider.tsx
339
+ function findSavedWalletAccount(wallets, savedWalletKey) {
340
+ if (!savedWalletKey) {
341
+ return;
342
+ }
343
+ const [savedWalletName, savedWalletAddress] = savedWalletKey.split(":");
344
+ if (!savedWalletName || !savedWalletAddress) {
345
+ return;
346
+ }
347
+ for (const wallet of wallets) {
348
+ if (wallet.name !== savedWalletName) continue;
349
+ for (const account of wallet.accounts) {
350
+ if (account.address === savedWalletAddress) {
351
+ return account;
352
+ }
353
+ }
354
+ }
355
+ }
356
+ function SelectedWalletAccountContextProvider({
357
+ children,
358
+ filterWallets,
359
+ stateSync
360
+ }) {
361
+ const wallets = react.useWallets();
362
+ const filteredWallets = React2__default.default.useMemo(() => wallets.filter(filterWallets), [wallets, filterWallets]);
363
+ const wasSetterInvokedRef = React2__default.default.useRef(false);
364
+ const [selectedWalletAccount, setSelectedWalletAccountInternal] = React2__default.default.useState(() => {
365
+ const savedWalletKey = stateSync.getSelectedWallet();
366
+ const savedWalletAccount = findSavedWalletAccount(filteredWallets, savedWalletKey);
367
+ return savedWalletAccount;
368
+ });
369
+ const setSelectedWalletAccount = React2__default.default.useCallback(
370
+ (setStateAction) => {
371
+ wasSetterInvokedRef.current = true;
372
+ setSelectedWalletAccountInternal((prevSelectedWalletAccount) => {
373
+ const nextWalletAccount = typeof setStateAction === "function" ? setStateAction(prevSelectedWalletAccount) : setStateAction;
374
+ return nextWalletAccount;
375
+ });
376
+ },
377
+ [setSelectedWalletAccountInternal]
378
+ );
379
+ React2__default.default.useEffect(() => {
380
+ if (!wasSetterInvokedRef.current) return;
381
+ const accountKey = selectedWalletAccount ? react.getUiWalletAccountStorageKey(selectedWalletAccount) : void 0;
382
+ if (accountKey) {
383
+ stateSync.storeSelectedWallet(accountKey);
384
+ } else {
385
+ stateSync.deleteSelectedWallet();
386
+ }
387
+ }, [selectedWalletAccount, stateSync]);
388
+ React2__default.default.useEffect(() => {
389
+ if (wasSetterInvokedRef.current) return;
390
+ const savedWalletKey = stateSync.getSelectedWallet();
391
+ const savedAccount = findSavedWalletAccount(filteredWallets, savedWalletKey);
392
+ if (savedAccount && selectedWalletAccount && react.uiWalletAccountsAreSame(savedAccount, selectedWalletAccount)) {
393
+ return;
394
+ }
395
+ if (savedAccount) {
396
+ setSelectedWalletAccountInternal(savedAccount);
397
+ }
398
+ }, [filteredWallets, stateSync, selectedWalletAccount]);
399
+ const walletAccount = React2__default.default.useMemo(() => {
400
+ if (!selectedWalletAccount) return;
401
+ for (const wallet of filteredWallets) {
402
+ for (const account of wallet.accounts) {
403
+ if (react.uiWalletAccountsAreSame(account, selectedWalletAccount)) {
404
+ return account;
405
+ }
406
+ }
407
+ if (react.uiWalletAccountBelongsToUiWallet(selectedWalletAccount, wallet) && wallet.accounts[0]) {
408
+ return wallet.accounts[0];
409
+ }
410
+ }
411
+ }, [selectedWalletAccount, filteredWallets]);
412
+ React2__default.default.useEffect(() => {
413
+ if (!selectedWalletAccount) return;
414
+ if (wasSetterInvokedRef.current) return;
415
+ if (!walletAccount) {
416
+ setSelectedWalletAccountInternal(void 0);
417
+ }
418
+ }, [selectedWalletAccount, walletAccount]);
419
+ return /* @__PURE__ */ React2__default.default.createElement(SelectedWalletAccountContext.Provider, { value: [walletAccount, setSelectedWalletAccount, filteredWallets] }, children);
420
+ }
322
421
 
422
+ exports.SelectedWalletAccountContext = SelectedWalletAccountContext;
423
+ exports.SelectedWalletAccountContextProvider = SelectedWalletAccountContextProvider;
424
+ exports.useSelectedWalletAccount = useSelectedWalletAccount;
323
425
  exports.useSignAndSendTransaction = useSignAndSendTransaction;
426
+ exports.useSignAndSendTransactions = useSignAndSendTransactions;
324
427
  exports.useSignIn = useSignIn;
325
428
  exports.useSignMessage = useSignMessage;
326
429
  exports.useSignTransaction = useSignTransaction;
430
+ exports.useSignTransactions = useSignTransactions;
327
431
  exports.useWalletAccountMessageSigner = useWalletAccountMessageSigner;
328
432
  exports.useWalletAccountTransactionSendingSigner = useWalletAccountTransactionSendingSigner;
329
433
  exports.useWalletAccountTransactionSigner = useWalletAccountTransactionSigner;