Appearance
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
:
Prop | Type | Description |
---|---|---|
createAdditional | boolean | If 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 totrue
- 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.
Prop | Type | Description |
---|---|---|
walletIndex | number | The 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 ifwalletIndex
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"
}
]
}