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.

Creating multiple HD wallets

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

tsx
const {createWallet} = usePrivy();

As an optional parameter to createWallet, you may pass an object containing the following fields:

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

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

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

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

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.

INFO

Privy does not yet support exporting the private key for a specific embedded wallet address. Alternatively, you may prompt users to export the seed phrase for their first embedded wallet, and then import that seed phrase into another wallet client (e.g. MetaMask) to automatically access all of their HD wallets.

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"
    }
  ]
}