The dynamic client provides an interface to easily interact with and create embedded wallets for your users.
Notice that embedded wallets must be enabled in your environment’s
dashboard settings first!
Creating and Getting the Wallet
This interface allows you to create new embedded wallets, check if any already exist and fetch the current wallet.
See the following example which renders a prompt to create a wallet or renders its address if it already exists:
import { dynamicClient } from '<path to client file>';
import { useReactiveClient } from '@dynamic-labs/react-hooks';
const EmbeddedWallet: FC = () => {
const { wallets } = useReactiveClient(dynamicClient)
const wallet = wallets.userWallets[0]
return (
<View>
{wallet && wallets.embedded.hasWallet ? (
<View>Your wallet address: {wallet.address}</View>
) : (
<Button onPress={() => wallets.embedded.createWallet()}>
Create Wallet
</Button>
)}
</View>
)
}
Creating a Wallet for a Specific Chain
The createWallet
method also allows you to create an embedded wallet for a specific chain
See the example below:
import { dynamicClient } from '<path to client file>';
import { useReactiveClient } from '@dynamic-labs/react-hooks';
const AddEvmWalletButton: FC = () => {
return (
<Button
title="Add EVM wallet"
onPress={() => dynamicClient
.wallets
.embedded
.createWallet({ chain: 'Evm' })
}
/>
)
}
Exporting Wallet Keys
If your app uses embedded wallets, you must also make sure to provide your users with some way to export their wallets’ keys
to ensure they have absolute control over them.
Dynamic provides a quick and easy way to do so with our embedded wallet key export UI flow!
V3/MPC wallets can only be exported as private keys. Recovery phrase export is not available for these wallet types.
This flow will export the keys from your primary wallet. See
here how
to set your primary wallet.
const ExportEmbeddedWalletKeyButtons: FC = () => {
return (
<View>
<TouchableOpacity
onPress={() =>
dynamicClient.ui.wallets.revealEmbeddedWalletKey({
type: 'private-key',
})
}
>
Reveal private key
</TouchableOpacity>
<TouchableOpacity
onPress={() =>
dynamicClient.ui.wallets.revealEmbeddedWalletKey({
type: 'recovery-phrase',
})
}
>
Reveal recovery phrase
</TouchableOpacity>
</View>
)
}
You can read more about the embedded wallets module here.
WaaS Operations
Dynamic’s Wallet as a Service (WaaS) provides advanced wallet management capabilities for embedded wallets. These operations allow you to perform secure key management, backup, and signing operations.
WaaS operations require the wallet to be a Dynamic WaaS wallet (wallet.key === 'dynamicwaas'
).
Backup Key Shares to Google Drive
Backup wallet key shares to Google Drive for secure recovery:
import { useMutation } from '@tanstack/react-query';
const BackupToGoogleDrive: FC = () => {
const client = useDynamicClient();
const { wallets } = client;
const wallet = wallets.userWallets[0]; // Assuming first wallet
const { mutate: backupKeyShares, isPending } = useMutation({
mutationFn: async ({ password }: { password?: string }) => {
await client.wallets.waas.backupKeySharesToGoogleDrive(wallet.id, {
accountAddress: wallet.address,
password, // optional password
});
},
});
return (
<Button
title="Backup to Google Drive"
disabled={isPending}
onPress={() => backupKeyShares({ password: 'optional-password' })}
/>
);
};
Import Private Key
Import an existing private key into a WaaS wallet:
import { useMutation } from '@tanstack/react-query';
const ImportPrivateKey: FC = () => {
const client = useDynamicClient();
const wallet = wallets.userWallets[0];
const { mutate: importKey, isPending } = useMutation({
mutationFn: async ({
privateKey,
thresholdSignatureScheme
}: {
privateKey: string;
thresholdSignatureScheme: string;
}) => {
await client.wallets.waas.importPrivateKey(wallet.id, {
privateKey,
thresholdSignatureScheme,
});
},
});
return (
<Button
title="Import Private Key"
disabled={isPending}
onPress={() => importKey({
privateKey: 'your-private-key',
thresholdSignatureScheme: 'TWO_OF_TWO'
})}
/>
);
};
Sign Raw Messages
Sign arbitrary raw messages with your WaaS wallet, message must be 64 characters, available only for EVM wallets:
import { useMutation } from '@tanstack/react-query';
const SignRawMessage: FC = () => {
const client = useDynamicClient();
const wallet = wallets.userWallets[0];
const { mutate: signMessage, data: signature, isPending } = useMutation({
mutationFn: async ({
message,
password
}: {
message: string;
password?: string;
}) => {
return await client.wallets.waas.signRawMessage(wallet.id, {
accountAddress: wallet.address,
message,
password, // optional password
});
},
});
return (
<View>
<Button
title="Sign Message"
disabled={isPending}
onPress={() => signMessage({
message: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', // 64-char hex
password: 'optional-password'
})}
/>
{signature && (
<Text>Signature: {signature}</Text>
)}
</View>
);
};
Update Wallet Password
Update the password for your WaaS wallet:
import { useMutation } from '@tanstack/react-query';
const UpdatePassword: FC = () => {
const client = useDynamicClient();
const wallet = wallets.userWallets[0];
const { mutate: updatePassword, isPending } = useMutation({
mutationFn: async ({
existingPassword,
newPassword
}: {
existingPassword: string;
newPassword: string;
}) => {
await client.wallets.waas.updatePassword(wallet.id, {
accountAddress: wallet.address,
existingPassword,
newPassword,
});
},
});
return (
<Button
title="Update Password"
disabled={isPending}
onPress={() => updatePassword({
existingPassword: 'current-password',
newPassword: 'new-password'
})}
/>
);
};
Refresh Wallet Account Shares
Refresh the account shares for your WaaS wallet:
import { useMutation } from '@tanstack/react-query';
const RefreshAccountShares: FC = () => {
const client = useDynamicClient();
const wallet = wallets.userWallets[0];
const { mutate: refreshShares, isPending } = useMutation({
mutationFn: async ({ password }: { password?: string }) => {
await client.wallets.waas.refreshWalletAccountShares(wallet.id, {
accountAddress: wallet.address,
password,
});
},
});
return (
<Button
title="Refresh Account Shares"
disabled={isPending}
onPress={() => refreshShares({ password: 'optional-password' })}
/>
);
};
All WaaS operations support optional password parameters for additional security.
The accountAddress
parameter should be the wallet’s address, and walletId
identifies the specific WaaS wallet instance.