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.