Skip to content

Hierarchical deterministic (HD) wallets

Privy embedded wallets are hierarchical deterministic (HD) wallets. An HD wallet allows you to generate multiple Ethereum addresses and private keys from a shared source of entropy: the wallet seed (or equivalently, a BIP-39 mnemonic encoding the seed, known as a seed phrase).

In kind, Privy can be used to provision multiple embedded wallets for a single user. Read more below to learn how!

Read more about how HD wallets work.

HD wallets use a shared source of entropy to derive the wallet seed; this entropy is protected by the Privy cryptosystem.

Each wallet is generated from the seed and a unique path parameter, which has the format:

m / purpose' / coin_type' / account' / change / address_index

For Privy's embedded wallets, the path used for the i-th wallet is:

m/44'/60'/0'/0/i

where i is 0-indexed. An HD wallet is said to have an index of i if it is derived from the i-th path above. You can read more about these derivation paths here.

INFO

Privy's React SDK currently only supports creating and using HD wallets on EVM networks. We are actively building support for HD wallets on Solana within the React SDK as well.

Creating multiple HD wallets

To create multiple HD wallets for a user, use the createWallet method from the usePrivy hook:

tsx
const {createWallet} = usePrivy();

Creating the user's first wallet

If this is the first wallet you are creating for the user (e.g. the 0th index), you may call createWallet with no parameters:

tsx
// Creating the first wallet for a user
await createWallet();

Creating additional wallets

There are two approaches to creating additional wallets.

1. Create an additional wallet with the next avaialable index

If the user already has an embedded wallet, and you are creating an additional embedded wallet, call createWallet with createAdditional set to true:

PropTypeDescription
createAdditionalbooleanIf true, will allow the user to create a wallet regardless if it is their first wallet or an additional wallet. If false, createWallet will succeed only if the use is creating their first wallet. Defaults to false.

Once invoked, createWallet will return a Promise that resolves to the Wallet created for the user at the specified index, if it was successful. This method will reject with an error if:

  • the user is not authenticated
  • the user already has an embedded wallet and createAdditional was not set to true
  • if there is another error during wallet creation, such as the user exiting prematurely
tsx
// Creating additional embedded wallets for the user
// You can also create the first wallet for the user using this syntax
await createWallet({createAdditional: true});

2. Create an additional wallet with a specified HD wallet index

To create a wallet at a specified HD wallet index, call createWallet with the preferred walletIndex. This method will either create a new wallet, or return the existing one if one already exists at the specified index.

PropTypeDescription
walletIndexnumberThe specified HD wallet index. Must be a positive number, and must be 0 for the user's first wallet.

INFO

A wallet with HD index 0 must be created before creating a wallet at greater HD indices.

tsx
// Create an additional embedded wallet at index 5
await createWallet({walletIndex: 5});

An error can be thrown if:

  • the user is not authenticated
  • wallet creation fails or the wallet cannot be added to the user's account.
  • an invalid HD wallet index is supplied, i.e. walletIndex is less than 0, or if walletIndex is greater than 0 while user has no wallet with HD index 0.

TIP

Since HD wallets use the same underlying wallet seed, the recovery method for the wallet is shared across all of a user's embedded wallets.

Using multiple HD wallets

Getting a specific embedded wallet

Once a user has one or more embedded wallets, the wallets are added to both linkedAccounts array of the user object and the array of connected wallets returned by useWallets.

To find a specific embedded wallet for the user, search the useWallets array for a wallet with walletClientType: 'privy' and an address that matches your desired address:

tsx
// Ensure the wallet address is checksummed per EIP55
const desiredAddress = 'insert-your-desired-address-in-EIP55-format';
const {wallets} = useWallets();
const desiredWallet = wallets.find(
  (wallet) => wallet.walletClientType === 'privy' && wallet.address === desiredAddress,
);

You can also get a list of all of the user's embedded wallets by filtering the useWallets array for entries with walletClientType: 'privy':

tsx
const embeddedWallets = wallets.filter((wallet) => wallet.walletClientType === 'privy');

Requesting signatures and transactions

Your app can then use Privy's native signature and transaction methods, the wallet's EIP1193 provider, or a third-party library like viem or ethers, per the instructions below.

Using Privy's native signature and transaction methods

To use Privy's native signMessage, signTypedData, and sendTransaction methods with a specific embedded wallet, simply pass the address for your desired wallet as the final optional parameter to these methods:

tsx
const {signMessage} = usePrivy();
const signature = await signMessage(
  'insert-message-to-sign',
  insertOptionalUIConfigOrUndefined,
  desiredWallet.address, // Replace with the address of the desired embedded wallet
);

Using the EIP1193 provider, viem, and ethers

You can also request signatures and transactions from a specific embedded wallet using the wallet's EIP1193 provider or a library like viem or ethers.

To get the EIP1193 provider for a specific embedded wallet, first find the corresponding ConnectedWallet object from the useWallets array:

tsx
// Ensure the wallet address is checksummed per EIP55
const address = 'insert-your-desired-address-in-EIP55-format';
const {wallets} = useWallets();
const wallet = wallets.find(
  (wallet) => wallet.walletClientType === 'privy' && wallet.address === address,
);

Then, call the object's getEthereumProvider method to get an EIP1193 provider for that wallet:

tsx
const provider = await wallet.getEthereumProvider();

You can then easily pass that EIP1193 provider to a library like viem or ethers to use those libraries' interfaces to send requests to the wallet.

Exporting HD wallets

To export the private key or seed phrase for a specific HD wallet, simply pass the address of the wallet you'd like to export as an address parameter to the exportWallet method:

tsx
const {exportWallet} = usePrivy();
await exportWallet({address: 'insert-your-desired-address'});

If no address is passed to exportWallet, Privy will default to exporting the non-imported wallet at walletIndex: 0.

Pregenerating multiple HD wallets

Privy supports pregenerating multiple HD wallets when creating new users. With our user import endpoint, you can create a user with up to 10 pregenerated HD wallets. Simply call the import endpoint with create_n_ethereum_wallets set to the number of embedded wallets you want to generate for your user.

INFO

Pregeneration endpoints have heavier rate limit of 240 users per minute. If you are being rate limited, responses will have status code 429. We suggest you setup exponential back-offs starting at 1 second to seamlessly recover.

Below is a sample cURL command for pregenerating two new wallets for a user with Privy:

bash
$ curl --request POST https://auth.privy.io/api/v1/users \
-u "<your-privy-app-id>:<your-privy-app-secret>" \
-H "privy-app-id: <your-privy-app-id>" \
-H 'Content-Type: application/json' \
-d '{
  "create_n_ethereum_wallets": 2,
  "linked_accounts": [
    {
      "address": "[email protected]",
      "type": "email"
    }
  ]
}'

A successful response will include the new user object along with their Privy user ID and embedded wallet addresses, like below. The generated wallets will be available to the user upon sign in.

Below is a sample successful response for generating two new wallets for a user with Privy:

json
{
  "id": "did:privy:clddy332f002tyqpq3b3lv327",
  "created_at": 1674788927,
  "linked_accounts": [
    {
      "address": "[email protected]",
      "type": "email"
    },
    {
      "address": "0x3DAF84b3f09A0E2092302F7560888dBc0952b7B7",
      "type": "wallet",
      "wallet_index": 0,
      "walletClient": "privy",
      "chain_type": "ethereum"
    },
    {
      "address": "0x1a235d54C58d0B5E339c784Fd98d4D71125fEb1c",
      "type": "wallet",
      "wallet_index": 1,
      "walletClient": "privy",
      "chain_type": "ethereum"
    }
  ]
}