Skip to content

Using Solana wallets

Getting the address

Once a user has created their embedded Solana wallet, you can get the user's address by inspecting the publicKey field of the wallet interface returned by useEmbeddedSolanaWallet:

tsx
// This assumes you have already created a Solana wallet for the user
const wallet = useEmbeddedSolanaWallet();
console.log(wallet.publicKey);
// 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, call the getProvider method on the wallet interface returned by useEmbeddedSolanaWallet:

tsx
// This assumes you have already created a Solana wallet for the user
const wallet = useEmbeddedSolanaWallet();
const provider = await wallet.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 wallet = useEmbeddedSolanaWallet();
const provider = await wallet.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.

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(solanaWallet.publicKey),
);

Sending a transaction

In addition to signing messages, you can use Privy embedded wallets to sign and send transactions on the Solana blockchain.

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 {useEmbeddedSolanaWallet} from '@privy-io/expo';

...

// The rest of this code must be placed within a React component
// Get Solana wallet
const solanaWallet = useEmbeddedSolanaWallet();
if (solanaWallet.status !== 'connected') 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.