Appearance
Using Solana wallets
Getting the Solana address
Once a user has created their embedded Solana wallet, to find the user's Solana address, first find the WalletWithMetadata
object from the user's user.linkedAccounts
array with walletClientType: 'privy'
and chainType: 'solana'
:
tsx
const {user} = usePrivy();
const wallet = user.linkedAccounts.find(
(account): account is WalletWithMetadata =>
account.type === 'wallet' &&
account.walletClientType === 'privy' &&
account.chainType === 'solana',
);
Next, inspect the address
field of the WalletWithMetadata
object to get the user's address:
tsx
console.log(wallet.address);
// 4tFqt2qzaNsnZqcpjPiyqYw9LdRzxaZdX2ewPncYEWLA
Getting a wallet provider
To send signature and transaction requests to a user's Solana wallet, you must first get a provider instance for their wallet. This serves as a standardized abstraction to communicate requests from your app to the user's wallet.
To get a provider for the user's Solana wallet, first get the wallets
array returned by Privy's useSolanaWallets
hook. This is an array of ConnectedSolanaWallet
objects that represent the active connection with the Solana embedded wallet.
tsx
const {wallets} = useSolanaWallets();
Currently, this array only contains the user's embedded wallet, if one has been created for a user. You can get the ConnectedSolanaWallet
object for the embedded wallet by accessing index 0
of this array.
TIP
In addition to the WalletWithMetadata
object, you can also get the user's Solana address by inspecting the address
field of the ConnectedSolanaWallet
object.
The former serves as a static representation of the user's Solana account, while the latter represents the active connection to the embedded wallet to prompt signatures in your app.
Finally, to get a Solana provider for the wallet, call the getProvider
method on the ConnectedSolanaWallet
object:
tsx
const provider = await wallets[0]?.getProvider();
The returned provider
implements a method called request
that allows your app to send arbitrary signature requests to the wallet. As a parameter to request
, you should pass an object with the following fields:
Field | Type | Description |
---|---|---|
method | 'signMessage' | The JSON-RPC method for the Solana wallet to execute. Currently, only 'signMessage' is supported as a method . |
params | any | The parameters that the wallet should use to execute the method . |
INFO
Privy's provider interface for the embedded Solana wallet is designed to have the same shape as Phantom's widely used PhantomProvider
. As Privy expands Solana functionality, our Solana provider will over time support the full set of methods as the PhantomProvider
.
Requesting a signature
To request a signature from a user's Solana wallet, first get a provider instance for the wallet:
tsx
// This assumes you have already created a Solana wallet for the user
const {wallets} = useSolanaWallets();
const provider = await wallets[0]?.getProvider();
Then, using the provider's request
method, send a request to the wallet with following fields in the request object:
method
:'signMessage'
params
: a JSON object containing amessage
to be signed.- If the message is a
string
, you should pass thestring
as themessage
directly. - If the message is an array of bytes (
Uint8Array
), you should base64-encode the array as astring
before passing it tomessage
. This may be particularly useful for signing transaction data.
- If the message is a
TIP
When request
is invoked, Privy will open up a modal where your user can view the message being signed and confirm the signature. You can choose to disable this UI and show your own UI instead by disabling wallet UIs in the Privy Dashbnoard for your app.
As an example, you might request a signature from a user's Solana wallet like so:
tsx
const message = 'Hello world';
const {signature} = await provider.request({
method: 'signMessage',
params: {
message: message,
},
});
The request will then return an object containing an Ed25519 signature
over the message
, as a base64-encoded string
. You can verify the signature as follows:
tsx
import bs58 from 'bs58';
import {Buffer} from 'buffer';
import nacl from 'tweetnacl';
// This assumes the `message` was originally a string
const result = nacl.sign.detached.verify(
Buffer.from(message, 'base64'),
Buffer.from(signature, 'base64'),
bs58.decode(wallets[0].address),
);
Sending a transaction
In addition to signing messages, you can use Privy embedded wallets to sign and send transactions on the Solana blockchain. Privy supports both legacy and v0 (versioned) transactions.
At a high-level, this involves preparing and serializing your Solana transaction, requesting a signature from the Privy embedded wallet, and submitting your signed transaction to the network.
As an example, you can send a transaction on Solana with Privy embedded wallets like so:
tsx
import {PublicKey, Transaction, Connection, SystemProgram} from '@solana/web3.js';
import {useSolanaWallets} from '@privy-io/react-auth';
...
// The rest of this code must be placed within a React component
// Get Solana wallet
const {wallets} = useSolanaWallets();
const solanaWallet = wallets[0];
// Build transaction request. You should adapt this logic for the particular
// type of transaction you need.
if (!solanaWallet) return;
// The `transaction` below is a legacy `Transaction` object from `@solana/web3.js`
// https://solana-labs.github.io/solana-web3.js/classes/Transaction.html
const serializedTransaction = transaction.serializeMessage();
// Sign the serialized transaction using Privy's signMessage
const provider = await solanaWallet.getProvider();
const {signature} = await provider.request({
method: "signMessage",
params: {
// Base64-encode serialized transaction before passing to Privy
message: serializedTransaction.toString('base64'),
},
});
// Add the signature to the transaction. Make sure to base64 decode the signature.
transaction.addSignature(sender, Buffer.from(signature, 'base64'));
if (!transaction.verifySignatures()) {
throw new Error('Invalid signature.');
}
// Serialize the transaction again with the signature, and send it to the network
const signedTransaction = transaction.serialize();
const txHash = await connection.sendRawTransaction(signedTransaction);
await connection.confirmTransaction(txHash);
TIP
Privy is building out a more native interface for sending Solana transactions. In the interim, we recommend using the flow above to sign Solana transactions and send them using @solana/web3.js
.