Skip to content

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:

FieldTypeDescription
method'signMessage'The JSON-RPC method for the Solana wallet to execute. Currently, only 'signMessage' is supported as a method.
paramsanyThe 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 a message to be signed.
    • If the message is a string, you should pass the string as the message directly.
    • If the message is an array of bytes (Uint8Array), you should base64-encode the array as a string before passing it to message. This may be particularly useful for signing transaction data.

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.