@solana/react
v6.1.0
Published
React hooks for building Solana apps
Readme
@solana/react
This package contains React hooks for building Solana apps.
Hooks
useSignIn(uiWalletAccount, chain)
Given a UiWallet or UiWalletAccount this hook returns a function that you can call to trigger a wallet's ‘Sign In With Solana’ feature.
Example
import { useSignIn } from '@solana/react';
function SignInButton({ wallet }) {
const csrfToken = useCsrfToken();
const signIn = useSignIn(wallet);
return (
<button
onClick={async () => {
try {
const { account, signedMessage, signature } = await signIn({
requestId: csrfToken,
});
// Authenticate the user, typically on the server, by verifying that
// `signedMessage` was signed by the person who holds the private key for
// `account.publicKey`.
//
// Authorize the user, also on the server, by decoding `signedMessage` as the
// text of a Sign In With Solana message, verifying that it was not modified
// from the values your application expects, and that its content is sufficient
// to grant them access.
window.alert(`You are now signed in with the address ${account.address}`);
} catch (e) {
console.error('Failed to sign in', e);
}
}}
>
Sign In
</button>
);
}useWalletAccountMessageSigner(uiWalletAccount)
Given a UiWalletAccount, this hook returns an object that conforms to the MessageModifyingSigner interface of @solana/signers.
Example
import { useWalletAccountMessageSigner } from '@solana/react';
import { createSignableMessage } from '@solana/signers';
function SignMessageButton({ account, text }) {
const messageSigner = useWalletAccountMessageSigner(account);
return (
<button
onClick={async () => {
try {
const signableMessage = createSignableMessage(text);
const [signedMessage] = await messageSigner.modifyAndSignMessages([signableMessage]);
const messageWasModified = signableMessage.content !== signedMessage.content;
const signatureBytes = signedMessage.signatures[messageSigner.address];
window.alert(
`Signature bytes: ${signatureBytes.toString()}${
messageWasModified ? ' (message was modified)' : ''
}`,
);
} catch (e) {
console.error('Failed to sign message', e);
}
}}
>
Sign Message: {text}
</button>
);
}[!NOTE] The type
MessageModifyingSigneris returned from this hook instead ofMessageSignerorMessagePartialSigner. This is a conservative assumption based on the fact that your application can not control whether or not the wallet will modify the message before signing it.
useWalletAccountTransactionSigner(uiWalletAccount, chain)
Given a UiWalletAccount and a chain that begins with solana:, this hook returns an object that conforms to the TransactionModifyingSigner interface of @solana/signers.
Example
import { useWalletAccountTransactionSigner } from '@solana/react';
function SignTransactionButton({ account, transaction }) {
const transactionSigner = useWalletAccountTransactionSigner(account, 'solana:devnet');
return (
<button
onClick={async () => {
try {
const [{ signatures }] = await transactionSigner.modifyAndSignTransactions([transaction]);
const signatureBytes = signatures[transactionSigner.address];
window.alert(`Signature bytes: ${signatureBytes.toString()}`);
} catch (e) {
console.error('Failed to sign transaction', e);
}
}}
>
Sign Transaction
</button>
);
}[!NOTE] The type
TransactionModifyingSigneris returned from this hook instead ofTransactionSignerorTransactionPartialSigner. This is a conservative assumption based on the fact that your application can not control whether or not the wallet will modify the transaction before signing it (eg. to add guard instructions, or a priority fee budget).
useWalletAccountTransactionSendingSigner(uiWalletAccount, chain)
Given a UiWalletAccount and a chain that begins with solana:, this hook returns an object that conforms to the TransactionSendingSigner interface of @solana/signers.
Example
import { useWalletAccountTransactionSendingSigner } from '@solana/react';
import {
appendTransactionMessageInstruction,
createSolanaRpc,
getBase58Decoder,
pipe,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
signAndSendTransactionMessageWithSigners,
} from '@solana/kit';
function RecordMemoButton({ account, rpc, text }) {
const signer = useWalletAccountTransactionSendingSigner(account, 'solana:devnet');
return (
<button
onClick={async () => {
try {
const { value: latestBlockhash } = await createSolanaRpc('https://api.devnet.solana.com')
.getLatestBlockhash()
.send();
const message = pipe(
createTransactionMessage({ version: 'legacy' }),
m => setTransactionMessageFeePayerSigner(signer, m),
m => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
m => appendTransactionMessageInstruction(getAddMemoInstruction({ memo: text }), m),
);
const signatureBytes = await signAndSendTransactionMessageWithSigners(message);
const base58Signature = getBase58Decoder().decode(signature);
window.alert(`View transaction: https://explorer.solana.com/tx/${base58Signature}?cluster=devnet`);
} catch (e) {
console.error('Failed to record memo', e);
}
}}
>
Record Memo
</button>
);
}useSignMessage(uiWalletAccount)
Given a UiWalletAccount, this hook returns a function you can call to sign a byte array.
Arguments
A config object with the following properties:
message: AUint8Arrayof bytes to sign
Returns
An object with the following properties:
signature: The 64-byte Ed25519 signature as aUint8ArraysignedMessage: TheUint8Arrayof bytes signed by the wallet. These bytes might differ from the input bytes if the wallet modified the message
Example
import { useSignMessage } from '@solana/react';
function SignMessageButton({ account, messageBytes }) {
const signMessage = useSignMessage(account);
return (
<button
onClick={async () => {
try {
const { signature } = await signMessage({
message: messageBytes,
});
window.alert(`Signature bytes: ${signature.toString()}`);
} catch (e) {
console.error('Failed to sign message', e);
}
}}
>
Sign Message
</button>
);
}useSignTransaction(uiWalletAccount, chain)
Given a UiWalletAccount and a chain that begins with solana:, this hook returns a function you can call to sign a serialized transaction.
Arguments
A config object with the following properties:
options: An object with the following properties: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.
transaction: AUint8Arrayof bytes that conforms to the Solana transaction schema
Returns
An object with the following properties:
signedTransaction: AUint8Arrayof bytes that conforms to the Solana transaction schema
Example
import { useSignTransaction } from '@solana/react';
function SignTransactionButton({ account, transactionBytes }) {
const signTransaction = useSignTransaction(account, 'solana:devnet');
return (
<button
onClick={async () => {
try {
const { signedTransaction } = await signTransaction({
transaction: transactionBytes,
});
window.alert(`Signed transaction bytes: ${signedTransaction.toString()}`);
} catch (e) {
console.error('Failed to sign transaction', e);
}
}}
>
Sign Transaction
</button>
);
}useSignAndSendTransaction(uiWalletAccount, chain)
Given a UiWalletAccount and a chain that begins with solana:, this hook returns a function you can call to sign and send a serialized transaction.
Arguments
A config object with the following properties:
options: An object with the following properties: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.
transaction: AUint8Arrayof bytes that conforms to the Solana transaction schema
Returns
That function returns an object with the following properties:
signature: AUint8Arrayof bytes representing the signature of the sent transaction.
Example
import { getBase58Decoder } from '@solana/codecs-strings';
import { useSignAndSendTransaction } from '@solana/react';
function SignAndSendTransactionButton({ account, transactionBytes }) {
const signAndSendTransaction = useSignAndSendTransaction(account, 'solana:devnet');
return (
<button
onClick={async () => {
try {
const { signature } = await signAndSendTransaction({
transaction: transactionBytes,
});
const base58TransactionSignature = getBase58Decoder().decode(signature);
window.alert(
`View transaction: https://explorer.solana.com/tx/${base58TransactionSignature}?cluster=devnet`,
);
} catch (e) {
console.error('Failed to send transaction', e);
}
}}
>
Sign and Send Transaction
</button>
);
}useSignTransactions(uiWalletAccount, chain)
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.
Arguments
One or more config objects with the following properties:
options: An object with the following properties: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.
transaction: AUint8Arrayof bytes that conforms to the Solana transaction schema
Returns
An array of objects with the following properties:
signedTransaction: AUint8Arrayof bytes that conforms to the Solana transaction schema
Example
import { useSignTransactions } from '@solana/react';
function SignTransactionsButton({ account, transactionBytes1, transactionBytes2 }) {
const signTransactions = useSignTransactions(account, 'solana:devnet');
return (
<button
onClick={async () => {
try {
const [{ signedTransaction: first }, { signedTransaction: second }] = await signTransactions(
{ transaction: transactionBytes1 },
{ transaction: transactionBytes2 },
);
window.alert(`Signed transaction bytes: ${first.toString()} and ${second.toString()}`);
} catch (e) {
console.error('Failed to sign transactions', e);
}
}}
>
Sign Transactions
</button>
);
}useSignAndSendTransactions(uiWalletAccount, chain)
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.
Arguments
One or more config objects with the following properties:
options: An object with the following properties: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.
transaction: AUint8Arrayof bytes that conforms to the Solana transaction schema
Returns
An array of objects with the following properties:
signature: AUint8Arrayof bytes representing the signature of each sent transaction.
Example
import { getBase58Decoder } from '@solana/codecs-strings';
import { useSignAndSendTransactions } from '@solana/react';
function SignAndSendTransactionsButton({ account, transactionBytes1, transactionBytes2 }) {
const signAndSendTransactions = useSignAndSendTransactions(account, 'solana:devnet');
return (
<button
onClick={async () => {
try {
const [first, second] = await signAndSendTransactions(
{ transaction: transactionBytes1 },
{ transaction: transactionBytes2 },
);
const [firstSignature, secondSignature] = [first.signature, second.signature].map(signature =>
getBase58Decoder().decode(signature),
);
window.alert(
`View transactions: https://explorer.solana.com/tx/${firstSignature}?cluster=devnet and https://explorer.solana.com/tx/${secondSignature}?cluster=devnet`,
);
} catch (e) {
console.error('Error returned by signAndSendTransactions', e);
}
}}
>
Sign and Send Transactions
</button>
);
}useSelectedWalletAccount()
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.
Arguments
This hook doesn't take any arguments.
Returns
The function returns an array consisting of the following elements in the order given:
SelectedWalletAccount: This element could be aUiWalletAccountorundefined, and represents the selected wallet account.SetSelectedWalletAccount: A setter function to set the SelectedWalletAccount state. It takes an argument which could be a callback function(prevState)=>newStateornewState.filteredWallets: List of filtered wallets using the function provided asfilterWalletfunction inSelectedWalletAccountContextProvider
Example
import React from 'react';
import { useSelectedWalletAccount } from '@solana/react';
function WalletInfo() {
const [selectedAccount, setSelectedAccount, filteredWallets] = useSelectedWalletAccount();
if (!selectedAccount) {
return <div>No wallet selected</div>;
}
return (
<div>
<p>Address: {selectedAccount.address}</p>
<button onClick={() => setSelectedAccount(undefined)}>Clear selection</button>
<p>Available wallets: {filteredWallets.length}</p>
</div>
);
}SelectedWalletAccountContextProvider
This is a react context provider for SelectedWalletAccountContext. It provides its children access to the context by using either useSelectedWalletAccount() or useContext(SelectedWalletAccountContext).
Props
The provider takes the following props:
filterWallet: a function used to filter supported wallets. For example you might use this to restrict your app to wallets that supportsolana:mainnet.stateSync: an object to store the selected wallet, with these properties: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}.getSelectedWallet: a function used to retrieve the persisted wallet account identifier from the persistent storage.deleteSelectedWallet: clears any persisted wallet account identifier from the persistent storage.
Example
import React from 'react';
import { SelectedWalletAccountContextProvider } from '@solana/react';
import type { UiWallet } from '@wallet-standard/react';
const STORAGE_KEY = 'solana-wallet-account-id';
export function App() {
return (
<SelectedWalletAccountContextProvider
filterWallet={(wallet: UiWallet) => wallet.accounts.length > 0}
stateSync={{
getSelectedWallet: () => localStorage.getItem(STORAGE_KEY),
storeSelectedWallet: accountKey => localStorage.setItem(STORAGE_KEY, accountKey),
deleteSelectedWallet: () => localStorage.removeItem(STORAGE_KEY),
}}
>
<WalletInfo />
</SelectedWalletAccountContextProvider>
);
}