Skip to main content

React Native Quickstart

This guide covers basic use cases and teaches you how to use our React Native SDK in your mobile app. For a more comprehensive guide of how to build your first dApp, check out our React Native starter tutorial.

What you will learn

  • How to use Mobile Wallet Adapter to:

    • Connect to a wallet app with transact.
    • Request wallet authorization and reauthorization.
    • Sign transactions and messages
  • How to use web3.js to:

    • Connect to a Solana RPC endpoint.
    • Construct a Solana transaction for signing.

Install libraries and polyfills

Follow React Native Setup or Expo Setup to install dependencies and polyfills.

Connect to a wallet

To connect to a wallet, use the transact function from @solana-mobile/mobile-wallet-adapter-protocol-web3js.

The transact method starts a session with a locally installed MWA-compatible wallet app. Within the callback, use wallet to send requests for signing or sending transactions/messages.

import { transact } from "@solana-mobile/mobile-wallet-adapter-protocol-web3js";

await transact(async (wallet) => {
/* ... */
});
tip

Use the transact function from @solana-mobile/mobile-wallet-adapter-protocol-web3js rather than @solana-mobile/mobile-wallet-adapter-protocol.

The former provides convenient wrappers around common web3.js Solana types like Transaction while the latter provides base64 encoded byte payloads.

Authorizing a wallet

After starting a session with a wallet app with transact, you should first request authorization for your app with a call to authorize.

When requesting authorization, include an App Identity to the request so users can recognize your app during the authorization flow.

  • name: The name of your app.
  • uri: The web URL associated with your app.
  • icon: A path to your app icon relative to the app uri above.
import {transact} from '@solana-mobile/mobile-wallet-adapter-protocol-web3js';
import {AuthorizeAPI} from '@solana-mobile/mobile-wallet-adapter-protocol';

export const APP_IDENTITY = {
name: 'React Native dApp',
uri: 'https://yourdapp.com'
icon: "favicon.ico", // Full path resolves to https://yourdapp.com/favicon.ico
};

const authorizationResult = await transact(async (wallet: AuthorizeAPI) => {
const authorizationResult = await wallet.authorize({
cluster: 'devnet',
identity: APP_IDENTITY,
});

return authorizationResult;
});

Once authorized with a wallet, the app can request the wallet to sign transactions, messages and send transactions via RPC. We'll cover that in the next section.

authorize returns an AuthorizationResult that contains:

  • accounts: An array of Accounts (a label and public key) from the wallet.
  • authToken: An authorization token that can be stored and re-used for requathorization on subsequent connections.

In practice, most wallet apps only support single account authorization, so there will be at most 1 item in accounts.

Reauthorization for subsequent connections

For subsequent connections to the wallet app, you can skip the authorization step by sending a reauthorize request with a previously stored authToken. If still valid, reauthorize will bypass the need to explicitly grant authorization again.

import {transact} from '@solana-mobile/mobile-wallet-adapter-protocol-web3js';
import {AuthorizeAPI, DeauthorizeAPI} from '@solana-mobile/mobile-wallet-adapter-protocol';

export const APP_IDENTITY = {
name: 'React Native dApp',
uri: 'https://yourdapp.com'
icon: "./favicon.ico",
};

// If we have one, retrieve an authToken from a previous authorization.
const storedAuthToken = maybeGetStoredAuthToken(); // dummy placeholder function

await transact(async (wallet: AuthorizeAPI & ReauthorizeAPI) => {
// If we have a previously stored authToken, we can instead call `reauthorize`.
const authorizationResult = await (storedAuthToken
? wallet.reauthorize({
auth_token: storedAuthToken,
identity: APP_IDENTITY,
})
: wallet.authorize({
cluster: 'devnet',
identity: APP_IDENTITY,
}));

// Rest of transact code goes below...
});

Deauthorizing a wallet

A dApp can revoke authorization or "disconnect" from a wallet by sending a deauthorize request. This will invalidate the previously provided authToken from the wallet.

await transact(async (wallet) => {
if (!previouslyStoredAuthToken) {
return;
}

// Pass in the prior auth token to invalidate it.
await wallet.deauthorize({ auth_token: previouslyStoredAuthToken });
});

Building Transactions

A client interacts with the Solana network by submitting a transaction to the cluster. Transactions allow a client to invoke instructions of on-chain Programs.

For a full explanation, see the core docs overview of a transaction.

A versioned transaction is a new format for transactions required for use by clients. We'll create a VersionedTransaction from the @solana/web3.js library.

As an example, we'll be invoking the transfer instruction from the System Program. The System Program is an example of a Solana Native Program.

import {
Connection,
PublicKey,
TransactionInstruction,
VersionedTransaction,
TransactionMessage,
SystemProgram,
} from "@solana/web3.js";

// Create a list of Program instructions to execute.
const instructions = [
SystemProgram.transfer({
fromPubkey: fromPublicKey,
toPubkey: toPublicKey,
lamports: 1_000,
}),
];

// Connect to an RPC endpoint and get the latest blockhash, to include in
// the transaction.
const latestBlockhash = await connection.getLatestBlockhash();

// Create the "message" of a transaction and compile to `V0Message` format.
const txMessage = new TransactionMessage({
payerKey: fromPublicKey,
recentBlockhash: latestBlockhash.blockhash,
instructions,
}).compileToV0Message();

// Construct the Versioned Transaction passing in the message.
const versionedTransaction = new VersionedTransaction(txMessage);

Signing Transactions

After creating a VersionedTransaction or Transaction, you can request a wallet to sign it within transact.

import { transact } from "@solana-mobile/mobile-wallet-adapter-protocol-web3js";
import { toByteArray } from "react-native-quick-base64";

const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const signedTx = await transact(async (wallet) => {
// Authorize the wallet session
const authorizationResult = await wallet.authorize({
cluster: "devnet",
identity: APP_IDENTITY,
});

// Convert base64 address to web3.js PublicKey class
const authorizedPubkey = new PublicKey(
toByteArray(authorizationResult.accounts[0].address)
);

// Construct an instruction to transfer 1,000 lamports to a randomly generated account
const randomKeypair = Keypair.generate();
const instructions = [
SystemProgram.transfer({
fromPubkey: authorizedPubkey,
toPubkey: randomKeypair.publicKey,
lamports: 1_000,
}),
];

// Connect to an RPC endpoint and get the latest blockhash, to include in
// the transaction.
const latestBlockhash = await connection.getLatestBlockhash();

// Construct the Versioned message and transaction.
const txMessage = new TransactionMessage({
payerKey: fromPublicKey,
recentBlockhash: latestBlockhash.blockhash,
instructions,
}).compileToV0Message();
const versionedTransaction = new VersionedTransaction(txMessage);

// Request to sign the transaction
const signedTxs = await wallet.signTransactions({
transactions: [versionedTransaction],
});

return signedTxs[0];
});

Signing messages

Mobile Wallet Adapter provides an API to request message signing. In this case, a message is any payload of bytes.

import {transact} from '@solana-mobile/mobile-wallet-adapter-protocol-web3js';

// Convert 'Hello world!' to a byte array.
const message = 'Hello world!'
const messageBuffer = new Uint8Array(
message.split('').map(c => c.charCodeAt(0)),
);

const signedMessages = await transact(async (wallet) => {
// Authorize the wallet session.
const authorizationResult = await wallet.authorize({
cluster: 'devnet',
identity: APP_IDENTITY,
});

// Sign the payload with the provided address from authorization.
const signedMessages = wallet.signMessages({
addresses: [authorizationResult.address].
payloads: [messageBuffer]
})

return signedMessages;
});

Send a Transaction

After a Transaction is signed by the appropriate accounts, it can be submitted to the Solana network via RPC.

import { transact } from "@solana-mobile/mobile-wallet-adapter-protocol-web3js";
import {
sendTransaction,
clusterApiUrl,
Connection,
VersionedTransaction,
confirmTransaction,
} from "@solana/web3.js";

const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const signedTx: VersionedTransaction = await transact((wallet) => {
/* ...signing code from above... */
});

// After sending, a transaction signature is returned.
const txSignature = await connection.sendTransaction(signedTx);

// Confirm the transaction was successful.
const confirmationResult = await connection.confirmTransaction(
txSignature,
"confirmed"
);

if (confirmationResult.value.err) {
throw new Error(JSON.stringify(confirmationResult.value.err));
} else {
console.log("Transaction successfully submitted!");
}

The result from sending a transaction is a base58 transaction signature (or transaction ID). This transaction signature can be used to uniquely identify your transaction on the ledger.

Using confirmTransaction, you can check that the transaction was confirmed by the network. For other commitment levels, read about Commitment Status.

Sign and Send with MWA

An alternative option for submitting transactions is for the dApp to send a signAndSendTransactions MWA request to a wallet.

This request sends an unsigned transaction to the wallet. If authorized, the wallet will then sign the transaction and send it to the network with its own implementation.

import { transact } from "@solana-mobile/mobile-wallet-adapter-protocol-web3js";
import {
sendTransaction,
clusterApiUrl,
Connection,
VersionedTransaction,
confirmTransaction,
} from "@solana/web3.js";

const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const txSignature = await transact((wallet) => {
/* ...transaction code from above... */

// Send the unsigned transaction, the wallet will sign and submit it to the network,
// returning the transaction signature.
const transactionSignatures = await wallet.signAndSendTransactions({
transactions: [versionedTransaction],
});

return transactionSignatures[0];
});

// Confirm the transaction was successful.
const confirmationResult = await connection.confirmTransaction(
txSignature,
"confirmed"
);

if (confirmationResult.value.err) {
throw new Error(JSON.stringify(confirmationResult.value.err));
} else {
console.log("Transaction successfully submitted!");
}

Next Steps

  • Follow the First dApp Tutorial to learn how to use our starter dApp scaffold, create UI components, and record a public message on the Solana Blockchain.

  • See our collection of Sample Apps to reference a full React Native app.

  • Dive into the Solana Program Library (SPL) to learn about more interesting Solana Programs, like the Token Program used to create NFTs!