Skip to content

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.

  1. Install @aptos-labs/wallet-adapter-react.

    Terminal window
    npm install @aptos-labs/wallet-adapter-react
    For 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-adapter

    In App.tsx or it’s equivalent, import the Aptos Wallet Adapter and any legacy Wallet plugins.

    Section titled “In 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";
    // ...
  2. Initialize the AptosWalletAdapterProvider.

    You can use any of the following optional fields.

    It is recommended to:

    1. Set autoConnect to true.
    2. 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
    FieldDescriptionExample
    autoConnectA prop indicates whether the dapp should auto connect with the most recently connected wallet on page reload.true
    dappConfigSpecify 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 }
    onErrorA callback function to fire when the adapter throws an error.(error) => { console.log("error", error); }
    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 (
    <AptosWalletAdapterProvider
    autoConnect={true}
    dappConfig={{
    network: Network.MAINNET,
    aptosApiKeys: {
    mainnet: process.env.APTOS_API_KEY_MAINNET,
    }
    }}
    onError={(error) => {
    console.log("error", error);
    }}
    >
    {children}
    </AptosWalletAdapterProvider>
    );
    };
  3. 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 adapter
    const { account, connected, wallet, changeNetwork } = useWallet();

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:

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.

FieldTypeDescription
connectedbooleanIndicates if the wallet is currently connected.
isLoadingbooleanIndicates if a wallet operation is currently loading.
account{ address: string; publicKey: string | string[]; minKeysRequired?: number; ansName?: string | null; } | nullCurrent account info or null if no account is connected.
network{ name: Network; chainId?: string; url?: string; } | nullCurrent network info or null if no network is selected.
wallet{ name: WalletName; icon: string; url: string; } | nullCurrent wallet info or null if no wallet is selected. Includes wallet name, icon, and URL.
walletsReadonlyArray<{ 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.

See WalletCore.ts in wallet-adapter-core for where these functions are implemented.

FunctionSignatureDescription
connectconnect(walletName: WalletName): voidConnects to the specified wallet by its name.
disconnectdisconnect(): voidDisconnects the currently connected wallet.
signTransactionsignTransaction(transactionOrPayload: AnyRawTransaction | Types.TransactionPayload, asFeePayer?: boolean, options?: InputGenerateTransactionOptions): Promise<AccountAuthenticator>Signs a transaction with optional parameters for fee payment.
submitTransactionsubmitTransaction(transaction: InputSubmitTransactionData): Promise<PendingTransactionResponse>Submits a transaction with the provided transaction data.
signAndSubmitTransactionsignAndSubmitTransaction(transaction: InputTransactionData): Promise<any>Signs and submits a transaction with the given input data.
signMessagesignMessage(message: SignMessagePayload): Promise<SignMessageResponse>Signs a message and returns the signature and other response info.
signMessageAndVerifysignMessageAndVerify(message: SignMessagePayload): Promise<boolean>Signs a message and verifies the signer.
changeNetworkchangeNetwork(network: Network): Promise<AptosChangeNetworkOutput>Requests a change in the connected network. This is not supported by all wallets.

See the next.js example dapp for a demonstration of how these components are used in practice:

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>
)
}

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() 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;

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>;

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:

  1. 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.

  2. 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 wallet
    return (
    <Button onClick={connect(wallet)}>{wallet.name}</Button>
    )
    }

    This code snippet ensures that the correct wallet object is displayed in the appropriate view.