Skip to content
🎉 Welcome to the new Aptos Docs! Click here to submit feedback!
BuildSDKsWallet AdapterAptos Wallet Standard

Aptos Wallet Standard

The wallet standard provides guidelines for interoperability between wallet types. This ensures dapp developers do not need to change their applications to handle different wallets. This standard offers a single interface for all dapp developers, allowing easy additions of new wallets and more users to each application. This interoperability allows users to choose which wallet they want without worrying about whether apps support their use cases.

In order to ensure interoperability across Aptos wallets, the following is required:

  1. Mnemonics - a set of words that can be used to derive account private keys
  2. dapp API - entry points into the wallet to support access to identity managed by the wallet
  3. Key rotation - the feature handling both the relationship around mnemonics and the recovery of accounts in different wallets

Mnemonics phrases

A mnemonic phrase is a multiple word phrase that can be used to generate account addresses. We recommend one mnemonic per account in order to handle key rotation better. However, some wallets may want to support one mnemonic to many accounts coming from other chains. To support both of these use cases, the Aptos wallet standard uses a Bitcoin Improvement Proposal (BIP44) to derive path for mnemonics to accounts.

Creating an Aptos account

Aptos account creation can be supported across wallets in the following manner:

  1. Generate a mnemonic phrase, for example with BIP39.
  2. Get the master seed from that mnemonic phrase.
  3. Use the BIP44-derived path to retrieve an account address (e.g. m/44'/637'/0'/0'/0')
/**
  * Creates new account with bip44 path and mnemonics,
  * @param path. (e.g. m/44'/637'/0'/0'/0')
  * Detailed description: {@link https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki}
  * @param mnemonics.
  * @returns AptosAccount
  */
  static fromDerivePath(path: string, mnemonics: string): AptosAccount {
   if (!AptosAccount.isValidPath(path)) {
     throw new Error("Invalid derivation path");
   }
 
   const normalizeMnemonics = mnemonics
     .trim()
     .split(/\s+/)
     .map((part) => part.toLowerCase())
     .join(" ");
 
   const { key } = derivePath(path, bytesToHex(bip39.mnemonicToSeedSync(normalizeMnemonics)));
 
   return new AptosAccount(new Uint8Array(key));
}

Supporting one mnemonic per multiple account wallets

This is not recommended because the one-mnemonic-to-many-accounts paradigm makes it harder to handle rotated keys (the mnemonic changes for one account but not others). However, many wallets from other ecosystems use this paradigm, and take these steps to generate accounts

  1. Generate a mnemonic phrase, for example with BIP39.
  2. Get the master seed from that mnemonic phrase.
  3. Use the BIP44-derived path to retrieve private keys (e.g. m/44'/637'/i'/0'/0') where i is the account index.
  1. Increase i until all the accounts the user wants to import are found.
  • Note: The iteration should be limited, if an account doesn’t exist during iteration, keep iterating for a constant address_gap_limit (10 for now) to see if there are any other accounts. If an account is found we will continue to iterate as normal.

i.e.

const gapLimit = 10;
let currentGap = 0;
 
for (let i = 0; currentGap < gapLimit; i += 1) {
  const derivationPath = `m/44'/637'/${i}'/0'/0'`;
  const account = fromDerivePath(derivationPath, mnemonic);
  const response = account.getResources();
  if (response.status !== 404) {
    wallet.addAccount(account);
    currentGap = 0;
  } else {
    currentGap += 1;
  }
}

Wallet and dapp communication

More important than account creation, is how wallets and dapps communicate.

Following AIP-62, the Wallet standard defines an API for wallet and application interactions.

Wallet Interface Standard

A wallet must implement a AptosWallet interface with the wallet provider info and features:

class MyWallet implements AptosWallet {
  url: string;
  version: "1.0.0";
  name: string;
  icon:
    | `data:image/svg+xml;base64,${string}`
    | `data:image/webp;base64,${string}`
    | `data:image/png;base64,${string}`
    | `data:image/gif;base64,${string}`;
  chains: AptosChain;
  features: AptosFeatures;
  accounts: readonly AptosWalletAccount[];
}

A wallet must implement a AptosWalletAccount interface that represents the accounts that have been authorized by the dapp.

enum AptosAccountVariant {
  Ed25519,
  MultiEd25519,
  SingleKey,
  MultiKey,
}
 
class AptosWalletAccount implements WalletAccount {
  address: string;
 
  publicKey: Uint8Array;
 
  chains: AptosChain;
 
  features: AptosFeatures;
 
  variant: AptosAccountVariant;
 
  label?: string;
 
  icon?:
    | `data:image/svg+xml;base64,${string}`
    | `data:image/webp;base64,${string}`
    | `data:image/png;base64,${string}`
    | `data:image/gif;base64,${string}`
    | undefined;
}

If the wallet is a web extension wallet (i.e installed through the chrome extension store), the wallet must register itself using the registerWallet method to notify the dapp it is ready to be used.

const myWallet = new MyWallet();
 
registerWallet(myWallet);

A wallet is considered a valid Aptos wallet if it implements the standard required features.

A wallet must throw a AptosWalletError. The standard requires to support Unauthorized and InternalError but a wallet can throw a custom AptosWalletError error

// Using the default message
if (error) {
  throw new AptosWalletError(AptosWalletErrorCode.Unauthorized);
}
// Using a custom message
if (error) {
  throw new AptosWalletError(
    AptosWalletErrorCode.Unauthorized,
    "My custom unauthorized message"
  );
}
// Using a custom error
if (error) {
  throw new AptosWalletError(-32000, "Invalid Input");
}

Dapp API

For a dapp to easily integrate with a wallet, it encouraged to use the Aptos Wallet Adapter Standard.

If for some reason, a dapp decides to implement a custom wallet integration:

A dapp uses the getAptosWallets() function which gets all the Aptos standard compatible wallets.

import { getAptosWallets } from "@aptos-labs/wallet-standard";
 
let { aptosWallets, on } = getAptosWallets();

On first load, and before the dapp has been loaded, it gets all the wallets that have been registered so far. To keep getting all the registered wallets after this point, the dapp must add an event listener for new wallets that get registered receiving an unsubscribe function, which it can later use to remove the listener.

const removeRegisterListener = on("register", function () {
  // The dapp can add new aptos wallets to its own state context as they are registered
  let { aptosWallets } = getAptosWallets();
});
 
const removeUnregisterListener = on("unregister", function () {
  let { aptosWallets } = getAptosWallets();
});

The dapp has an event listener now, so it sees new wallets immediately and doesn’t need to poll or list them again. This also works if the dapp loads before any wallets (it will initialize, see no wallets, then see wallets as they load)

A dapp makes a wallet request by calling the feature name that coresponds to the desired action. For example, to use the connect feature:

const onConnect = () => {
  this.wallet.features["aptos:connect"].connect();
};

Key rotation

Key rotation is currently not implemented in any wallets. Mapping of rotated keys has been implemented, but SDK integration is in progress.

Wallets that import a private key will have to do the following:

  1. Derive the authentication key.
  2. Lookup the authentication key on-chain in the Account origination table.
  • If the account doesn’t exist, it’s a new account. The address to be used is the authentication key.
  • If the account does exist, it’s a rotated key account, and the address to be used will come from the table.