Wallet Adapter for Dapp Builders
Aptos provides a React Provider
and Context
for connecting Aptos wallets to your dapp. This Provider
allows you to specify which Wallets you want to allow connections to. Then you can use the Provider
to look up account information and sign transactions / messages.
This provides a standard interface for using all Aptos wallets, and allows new wallets to easily be supported just by updating your React Wallet Adapter dependency version.
Using the React Provider
and Context
Section titled “Using the React Provider and Context”-
Install @aptos-labs/wallet-adapter-react.
Terminal window npm install @aptos-labs/wallet-adapter-reactFor versions prior to v4.0.0
(Optional) Install the plugins for any “Legacy Standard Compatible” Wallets you want to support from this list.
Section titled “(Optional) Install the plugins for any “Legacy Standard Compatible” Wallets you want to support from this list.”For wallets that have not updated to using the AIP-62 standard, their plugins must be installed and passed in to the
Provider
manually.For example:
Terminal window npm i @okwallet/aptos-wallet-adapterIn
Section titled “In App.tsx or it’s equivalent, import the Aptos Wallet Adapter and any legacy Wallet plugins.”App.tsx
or it’s equivalent, import the Aptos Wallet Adapter and any legacy Wallet plugins.import { AptosWalletAdapterProvider } from "@aptos-labs/wallet-adapter-react";// Import any additional wallet plugins. Ex.import { OKXWallet } from "@okwallet/aptos-wallet-adapter";// ... -
Initialize the AptosWalletAdapterProvider.
You can use any of the following optional fields.
It is recommended to:
- Set
autoConnect
totrue
. - Set the
dappConfig
with:
- The
network
property set to the network your dapp works with - The
aptosApiKeys
property set to the generated Api Key for the specified network
Field Description Example autoConnect
A prop indicates whether the dapp should auto connect with the most recently connected wallet on page reload. true
dappConfig
Specify an alternate network to work on. This prop only works for wallets which are NOT chrome extensions. If set, this object must include the name of the network the app is connected to. The object may include a aptosConnectDappId. { network: 'mainnet', aptosApiKeys:{}, aptosConnectDappId: undefined }
onError
A callback function to fire when the adapter throws an error. (error) => { console.log("error", error); }
Full Example
Section titled “Full Example”import { AptosWalletAdapterProvider } from "@aptos-labs/wallet-adapter-react";import { PropsWithChildren } from "react";import { Network } from "@aptos-labs/ts-sdk";export const WalletProvider = ({ children }: PropsWithChildren) => {return (<AptosWalletAdapterProviderautoConnect={true}dappConfig={{network: Network.MAINNET,aptosApiKeys: {mainnet: process.env.APTOS_API_KEY_MAINNET,}}}onError={(error) => {console.log("error", error);}}>{children}</AptosWalletAdapterProvider>);}; - Set
-
Import useWallet in files where you want to access data from the Provider.
import { useWallet } from "@aptos-labs/wallet-adapter-react";// Access fields / functions from the adapterconst { account, connected, wallet, changeNetwork } = useWallet();
Choose a UI Package
Section titled “Choose a UI Package”The Wallet Adapter repository provides several UI packages to simplify allowing users to connect and select a wallet.
For UI components that work out of the box, but are less customizable, choose one of:
- Ant Design
- MUI (Material UI)
Otherwise, you should use the shadcn/ui wallet selector, as it has the most customization options. For more details on how to customize this wallet selector or build your own wallet selector, see this guide.
useWallet
Fields and Functions
Section titled “useWallet Fields and Functions”Fields
Section titled “Fields”Field | Type | Description |
---|---|---|
connected | boolean | Indicates if the wallet is currently connected. |
isLoading | boolean | Indicates if a wallet operation is currently loading. |
account | { address: string; publicKey: string | string[]; minKeysRequired?: number; ansName?: string | null; } | null | Current account info or null if no account is connected. |
network | { name: Network; chainId?: string; url?: string; } | null | Current network info or null if no network is selected. |
wallet | { name: WalletName; icon: string; url: string; } | null | Current wallet info or null if no wallet is selected. Includes wallet name, icon, and URL. |
wallets | ReadonlyArray<{ name: WalletName; url: string; icon: string; readyState: WalletReadyState.NotDetected; isAIP62Standard: true; }> | List of available wallets, including standard supported ones, each with name, URL, icon, readiness state, and AIP62 standard compliance indication. |
Functions
Section titled “Functions”See WalletCore.ts
in wallet-adapter-core
for where these functions are implemented.
Function | Signature | Description |
---|---|---|
connect | connect(walletName: WalletName): void | Connects to the specified wallet by its name. |
disconnect | disconnect(): void | Disconnects the currently connected wallet. |
signTransaction | signTransaction(transactionOrPayload: AnyRawTransaction | Types.TransactionPayload, asFeePayer?: boolean, options?: InputGenerateTransactionOptions): Promise<AccountAuthenticator> | Signs a transaction with optional parameters for fee payment. |
submitTransaction | submitTransaction(transaction: InputSubmitTransactionData): Promise<PendingTransactionResponse> | Submits a transaction with the provided transaction data. |
signAndSubmitTransaction | signAndSubmitTransaction(transaction: InputTransactionData): Promise<any> | Signs and submits a transaction with the given input data. |
signMessage | signMessage(message: SignMessagePayload): Promise<SignMessageResponse> | Signs a message and returns the signature and other response info. |
signMessageAndVerify | signMessageAndVerify(message: SignMessagePayload): Promise<boolean> | Signs a message and verifies the signer. |
changeNetwork | changeNetwork(network: Network): Promise<AptosChangeNetworkOutput> | Requests a change in the connected network. This is not supported by all wallets. |
Code Examples
Section titled “Code Examples”See the next.js example dapp for a demonstration of how these components are used in practice:
wallets
Section titled “wallets”wallets
is a list of available wallets, including standard supported ones, each with name, URL, icon, readiness state, and AIP62 standard compliance indication.
import { useWallet } from '@aptos-labs/wallet-adapter-react';
const displayInstalledWalletsDemo = () => {
const { wallets } = useWallet();
return ( <div> {wallets.map(wallet => { return <p>{wallet.name}</p> })} </div> )}
Support for Uninstalled Wallets
Section titled “Support for Uninstalled Wallets”Following AIP-62, the adapter uses an event-based communication model between a wallet and a dapp. This means only wallets installed in the user’s browser are detected automatically and available for use. To support the full Aptos wallet ecosystem, the adapter maintains a registry of supported wallets—allowing dapps to also display uninstalled wallets. It also exposes a utility function to easily manage all wallets.
import { useWallet, groupAndSortWallets } from '@aptos-labs/wallet-adapter-react';
const displayAllWalletsDemo = () => {
const { wallets = [], notDetectedWallets = [] } = useWallet();
const { aptosConnectWallets, availableWallets, installableWallets } = groupAndSortWallets( [...wallets, ...notDetectedWallets] );
return ( <div> /** Wallets that use social login to create an account on the blockchain */ {aptosConnectWallets.map((aptosConnectwallet) => ( return <p>{aptosConnectwallet.name}</p> ))} /** Wallets that are currently installed or loadable. */ {availableWallets.map((availableWallet) => ( return <p>{availableWallet.name}</p> ))} /** Wallets that are NOT currently installed or loadable. */ {installableWallets.map((installableWallet) => ( return <p>{installableWallet.name}</p> ))} </div> )}
connect()
and disconnect()
Section titled “connect() and disconnect()”connect()
establishes a connection between the dapp and a Wallet. You can then use disconnect()
to
import React from 'react';import { WalletName, useWallet } from '@aptos-labs/wallet-adapter-react';
const WalletConnectDemo = () => { const { connect, disconnect, account, connected } = useWallet();
const handleConnect = async () => { try { // Change below to the desired wallet name instead of "Petra" await connect("Petra" as WalletName<"Petra">); console.log('Connected to wallet:', account); } catch (error) { console.error('Failed to connect to wallet:', error); } };
const handleDisconnect = async () => { try { await disconnect(); console.log('Disconnected from wallet'); } catch (error) { console.error('Failed to disconnect from wallet:', error); } };
return ( <div> <h1>Aptos Wallet Connection</h1> <div> {connected ? ( <div> <p>Connected to: {account?.address}</p> <button onClick={handleDisconnect}>Disconnect</button> </div> ) : ( <button onClick={handleConnect}>Connect Wallet</button> )} </div> </div> );};
export default WalletConnectDemo;
signAndSubmitTransaction
Section titled “signAndSubmitTransaction”If you would like to separate out these steps, you can use signTransaction
and submitTransaction
separately instead.
import React from 'react';import { useWallet } from '@aptos-labs/wallet-adapter-react';import { Aptos, AptosConfig, Network } from '@aptos-labs/ts-sdk';
const config = new AptosConfig({ network: Network.MAINNET });const aptos = new Aptos(config);
const SignAndSubmit = () => { const { account, signAndSubmitTransaction } = useWallet();
const onSignAndSubmitTransaction = async () => { if(account == null) { throw new Error("Unable to find account to sign transaction") } const response = await signAndSubmitTransaction({ sender: account.address, data: { function: "0x1::aptos_account::transfer", functionArguments: [account.address, 1], }, }); // if you want to wait for transaction try { await aptos.waitForTransaction({ transactionHash: response.hash }); } catch (error) { console.error(error); } };
return ( <button onClick={onSignAndSubmitTransaction}> Sign and submit transaction </button> );};
export default SignAndSubmit;
signMessage
and verifyMessage
You can also use the shorthand signAndVerifyMessage
to create a message which can be verifiably from the connected wallet.
import React, { useState } from 'react';import { useWallet } from '@aptos-labs/wallet-adapter-react';
const SignMessageDemo = () => { const { signMessage, signMessageAndVerify, connected, account } = useWallet(); const [message, setMessage] = useState<string>(''); const [nonce, setNonce] = useState<string>(''); const [signedMessage, setSignedMessage] = useState<any>(null); const [verificationResult, setVerificationResult] = useState<boolean | null>(null); const [error, setError] = useState<string | null>(null);
const handleSignMessage = async () => { setError(null); try { const response = await signMessage({ message, nonce }); setSignedMessage(response); } catch (err: any) { setError(`Failed to sign message: ${err.message}`); } };
const handleVerifyMessage = async () => { setError(null); try { const result = await signMessageAndVerify({ message, nonce }); setVerificationResult(result); } catch (err: any) { setError(`Failed to verify message: ${err.message}`); } };
return ( <div> <h1>Aptos Sign and Verify Message</h1> <div> {connected ? ( <div> <p>Connected to: {account?.address}</p> <div className="flex flex-col gap-4"> <textarea value={message} onChange={(e) => setMessage(e.target.value)} placeholder="Enter your message here" className="border rounded p-2" /> <input type="text" value={nonce} onChange={(e) => setNonce(e.target.value)} placeholder="Enter nonce (random string) here" className="border rounded p-2 mt-2" /> <button onClick={handleSignMessage} className="bg-blue-500 text-white rounded p-2 mt-2"> Sign Message </button> {signedMessage && ( <div> <h4>Signed Message</h4> <pre>{JSON.stringify(signedMessage, null, 2)}</pre> <button onClick={handleVerifyMessage} className="bg-green-500 text-white rounded p-2 mt-2"> Verify Message </button> </div> )} {verificationResult !== null && ( <div> <h4>Verification Result</h4> <p>{verificationResult ? 'Message is verified!' : 'Failed to verify message.'}</p> </div> )} {error && ( <div className="text-red-600"> <p>{error}</p> </div> )} </div> </div> ) : ( <p>Please connect your wallet to sign and verify messages.</p> )} </div> </div> );};
export default SignMessageDemo;
changeNetwork
(Not supported by all wallets)
Section titled “changeNetwork (Not supported by all wallets)”Some wallets only support mainnet, so they will not support changeNetwork
. If you are relying on this feature, ensure that you implement error handling for if a wallet that does not support changeNetwork
. Nightly is an example of a wallet which does support changeNetwork
.
import React from 'react';import { useWallet } from '@aptos-labs/wallet-adapter-react';import { Network } from '@aptos-labs/ts-sdk';
const ChangeNetworkDemo = () => { const { network, changeNetwork, wallet } = useWallet(); const isNetworkChangeSupported = wallet?.name === "Nightly";
const isValidNetworkName = () => { return network && Object.values<string>(Network).includes(network.name); };
return ( <div> <h4>Network Info</h4> <div> <div><strong>Network name</strong></div> <div> <span style={{ color: isValidNetworkName() ? 'green' : 'red' }}> {network?.name ?? 'Not Present'} </span> {` (Expected: ${Object.values<string>(Network).join(', ')})`} </div> <div><strong>URL</strong></div> <div> {network?.url ? ( <a href={network.url} target="_blank" rel="noreferrer"> {network.url} </a> ) : ( 'Not Present' )} </div> <div><strong>Chain ID</strong></div> <div>{network?.chainId ?? 'Not Present'}</div> </div> <div> <h4>Change Network</h4> <div> <label> <input type="radio" name="network" value={Network.DEVNET} checked={network?.name === Network.DEVNET} onChange={() => changeNetwork(Network.DEVNET)} disabled={!isNetworkChangeSupported} /> Devnet </label> <label> <input type="radio" name="network" value={Network.TESTNET} checked={network?.name === Network.TESTNET} onChange={() => changeNetwork(Network.TESTNET)} disabled={!isNetworkChangeSupported} /> Testnet </label> <label> <input type="radio" name="network" value={Network.MAINNET} checked={network?.name === Network.MAINNET} onChange={() => changeNetwork(Network.MAINNET)} disabled={!isNetworkChangeSupported} /> Mainnet </label> </div> {!isNetworkChangeSupported && ( <div> * {wallet?.name ?? 'This wallet'} does not support network change requests </div> )} </div> </div> );};
export default ChangeNetworkDemo;
signAndSubmitBCSTransaction(payload)
(Not supported by all wallets)
Section titled “signAndSubmitBCSTransaction(payload) (Not supported by all wallets)”This is similar to the signAndSubmit
logic, but uses a BCS format for the transaction data
.
const onSignAndSubmitBCSTransaction = async () => { const response = await signAndSubmitTransaction({ sender: account.address, data: { function: "0x1::aptos_account::transfer", functionArguments: [AccountAddress.from(account.address), new U64(1)], }, }); // if you want to wait for transaction try { await aptos.waitForTransaction({ transactionHash: response.hash }); } catch (error) { console.error(error); }};
<button onClick={onSignAndSubmitTransaction}> Sign and submit BCS transaction</button>;
Mobile support
Section titled “Mobile support”Since Chrome extensions are not supported in mobile browsers by default, the adapter maintains a registry
of undetected wallets, including a deeplinkProvider
property for wallets that support deep linking.
This allows the dapp to display wallets that aren’t detectable in a mobile browser view but can still be connected to by redirecting the user to an in-app browser view.
{ name: "Petra", url: "https://chromewebstore.google.com/detail/petra-aptos-wallet/ejjladinnckdgjemekebdpeokbikhfci?hl=en", icon: "data:image/png;base64,iVBOR...QmCC", readyState: WalletReadyState.NotDetected, isAIP62Standard: true, deeplinkProvider: "https://petra.app/explore?link=",}
To render wallets with deeplinkProvider
support in your dapp—assuming you’re not using the official adapter wallet selector UI—follow these steps:
-
Retrieve all compatible wallets and group them by wallet type
import { useWallet, groupAndSortWallets } from '@aptos-labs/wallet-adapter-react';const displayAllWalletsDemo = () => {const { wallets = [], notDetectedWallets = [] } = useWallet();const { aptosConnectWallets, availableWallets, installableWallets } =groupAndSortWallets([...wallets, ...notDetectedWallets]);return (<div>/** Wallets that use social login to create an account on the blockchain */{aptosConnectWallets.map((aptosConnectwallet) => (<WalletItemComponent wallet={aptosConnectwallet}/>))}/** Wallets that are currently installed or loadable. */{availableWallets.map((availableWallet) => (<WalletItemComponent wallet={availableWallets}/>))}/** Wallets that are NOT currently installed or loadable. */{installableWallets.map((installableWallet) => (<WalletItemComponent wallet={installableWallets}/>))}</div>)}This code snippet retrieves all wallets in the Aptos ecosystem that are supported by the wallet adapter.
-
Display uninstalled wallets with deep link support in mobile view.
To ensure we display only wallets that support deep linking on mobile, we can check both for
deepLinkProvider
support and the current view type.In the component that renders each wallet:
import { useWallet, WalletReadyState } from '@aptos-labs/wallet-adapter-react';const WalletItemComponent = (wallet) => {const { connect } = useWallet();// On mobile, extension wallets will never have a state of `Installed`const isWalletReady = wallet.readyState === WalletReadyState.Installed;// Check if the wallet supports mobile deep linking.const mobileSupport ="deeplinkProvider" in wallet && wallet.deeplinkProvider;// If the wallet is not installed, the user is in a redirectable view (i.e., mobile browser but not an in-app browser),// and the wallet does not support deep linking—do not display the wallet.if (!isWalletReady && isRedirectable() && !mobileSupport) return null;// Otherwise, display the walletreturn (<Button onClick={connect(wallet)}>{wallet.name}</Button>)}This code snippet ensures that the correct
wallet
object is displayed in the appropriate view.