Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt

Use this file to discover all available pages before exploring further.

Only available from v4.52.3 onwards.

Summary

The useWalletBackup hook provides methods to manage wallet backup functionality for Dynamic embedded wallets, allowing users to back up their wallet key shares to Google Drive. For more information on wallet backup, see the Google Drive backup documentation. The hook needs to be initialized within a child of DynamicContextProvider.

Functions

initBackupProcess

Initiates the wallet backup flow by opening the Dynamic modal and displaying the backup progress view to the user.
This method uses Dynamic’s UI.
Returns: void Errors:
  • Throws "user_not_logged_in" if the user is not authenticated
Example:
const { initBackupProcess } = useWalletBackup();

try {
  initBackupProcess();
} catch (error) {
  console.error('Failed to start backup:', error);
}

ensureGoogleLinked

Ensures that the user has linked their Google account. If not already linked, prompts the user to link their Google account via popup.
This method uses Dynamic’s UI.
Returns: Promise<boolean> - true if Google account is linked (or successfully linked), false if linking failed. Example:
const { ensureGoogleLinked } = useWalletBackup();

const isLinked = await ensureGoogleLinked();
if (isLinked) {
  console.log('Google account is linked');
} else {
  console.error('Failed to link Google account');
}

backupWallet

Backs up a single wallet’s key shares to Google Drive. Parameters:
  • walletToBackup: Object containing wallet information
    • address: string - The wallet address
    • chain: ChainEnum - The chain the wallet is associated with
Returns: Promise<boolean> - true if backup was successful, false otherwise. Example:
import { ChainEnum } from '@dynamic-labs/sdk-api-core';
const { backupWallet } = useWalletBackup();

const success = await backupWallet({
  address: '0x123...',
  chain: ChainEnum.Evm,
});

if (success) {
  console.log('Wallet backed up successfully');
}

backupAllWallets

Backs up all wallets (or a specified list of wallets) to Google Drive.
This method is headless.
Parameters:
  • wallets (optional): Array of wallet objects to back up. If not provided, backs up all wallets that need backup.
Returns: Promise<void> - Resolves when all backups complete. Automatically refreshes user data after completion. Example:
const { backupAllWallets } = useWalletBackup();

// Back up all pending wallets
await backupAllWallets();

// Back up specific wallets
await backupAllWallets([
  { address: '0x123...', chain: ChainEnum.Evm },
  { address: '0x456...', chain: ChainEnum.Evm },
]);

startBackup

Starts the backup process with progress tracking and error handling. This function backs up wallets sequentially and updates the backupState as it progresses.
This method is headless.
Parameters:
  • onComplete (optional): Callback function to execute when backup completes successfully
  • fromIndex (optional): Index to start backup from (useful for retrying after failure). Default is 0.
Returns: Promise<void> - Resolves when backup completes or fails. Example:
const { startBackup, backupState } = useWalletBackup();

await startBackup(() => {
  console.log('All wallets backed up!');
});

// Retry from failed index
if (backupState.hasError && backupState.failedIndex !== null) {
  await startBackup(undefined, backupState.failedIndex);
}

getWalletsBackupStatus

Returns the backup status for all WaaS wallets.
This method is headless.
Returns: WalletWithBackupStatus[] - Array of wallets with their backup status. Example:
const { getWalletsBackupStatus } = useWalletBackup();

const wallets = getWalletsBackupStatus();
wallets.forEach(wallet => {
  console.log(`${wallet.address}: ${wallet.status}`);
});

getWalletsToBackup

Returns only the wallets that still need to be backed up (status is ‘pending’).
This method is headless.
Returns: WalletToBackup[] - Array of wallets that need backup. Example:
const { getWalletsToBackup } = useWalletBackup();

const pendingWallets = getWalletsToBackup();
console.log(`${pendingWallets.length} wallets need backup`);

Properties

areAllWalletsBackedUp

A boolean value indicating whether all WaaS wallets have been backed up to Google Drive. Type: boolean Example:
const { areAllWalletsBackedUp } = useWalletBackup();

if (areAllWalletsBackedUp) {
  console.log('All wallets are safely backed up');
}

isGoogleLinked

A boolean value indicating whether the user has linked their Google account. Type: boolean Example:
const { isGoogleLinked } = useWalletBackup();

if (!isGoogleLinked) {
  console.log('User needs to link Google account for backup');
}

backupState

An object containing the current state of the backup operation, including progress and error information. Type: WalletOperationState Example:
const { backupState } = useWalletBackup();

console.log(`Progress: ${backupState.currentIndex}/${backupState.totalWallets}`);
if (backupState.hasError) {
  console.error(`Backup failed at index ${backupState.failedIndex}`);
}

lastBackupError

The error thrown by the most recent backupWallet or backupToCloudProvider call, or undefined if the last attempt succeeded (or no attempt has been made). Updates as React state — components re-render automatically when it changes. Inspect it during render to drive a post-flight recovery UI for the (rare) case where pre-flight readiness can’t predict that a Google Drive upload will fail — for example, legacy users whose stored OAuth scopes weren’t recorded. Pair with isInsufficientGoogleDriveScopesError to detect missing-scope failures and recover by re-prompting consent via useGoogleDriveBackupReadiness.
lastBackupError is React state and won’t be updated yet immediately after await backupWallet(...). Read it from render, not from the line right after the await.
Type: unknown Example:
import {
  useWalletBackup,
  useGoogleDriveBackupReadiness,
  isInsufficientGoogleDriveScopesError,
  CloudBackupProvider,
} from '@dynamic-labs/sdk-react-core';

function BackupRecovery({ wallet }) {
  const { backupWallet, lastBackupError, clearBackupError } = useWalletBackup();
  const { requestAccess } = useGoogleDriveBackupReadiness();

  const needsScopeRecovery =
    lastBackupError && isInsufficientGoogleDriveScopesError(lastBackupError);

  if (!needsScopeRecovery) return null;

  const onRecover = async () => {
    clearBackupError();
    const r = await requestAccess();
    if (r.status === 'ready') {
      await backupWallet(wallet, CloudBackupProvider.GoogleDrive);
    }
  };

  return (
    <button onClick={onRecover}>Re-grant Drive access and retry</button>
  );
}

clearBackupError

Clears lastBackupError back to undefined. Call this after handling the error so subsequent renders don’t re-trigger your fallback logic. Returns: void

Scope helpers and error guards

The same module also exports a few utilities for callers building their own pre-flight or post-flight checks against the Google Drive backup flow.

GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES

The readonly tuple of OAuth scopes that must be granted on the user’s Google account for the backup upload to succeed.
import { GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES } from '@dynamic-labs/sdk-react-core';
// [
//   'https://www.googleapis.com/auth/drive.appdata',
//   'https://www.googleapis.com/auth/drive.file',
// ]

findMissingGoogleDriveBackupScopes

Given the scopes the user has granted, returns the subset of required scopes that are still missing. Parameters:
  • grantedScopes: readonly string[]
Returns: string[]

hasAllGoogleDriveBackupScopes

Convenience boolean — true if grantedScopes covers every required scope. Parameters:
  • grantedScopes: readonly string[]
Returns: boolean

isInsufficientGoogleDriveScopesError

Type guard for Google Drive backup failures caused by missing or insufficient OAuth scopes. Recover by re-prompting consent (e.g. via useGoogleDriveBackupReadiness().requestAccess()) and retrying the backup. Parameters:
  • err: unknown
Returns: err is GoogleDriveBackupAccessError
import { isInsufficientGoogleDriveScopesError } from '@dynamic-labs/sdk-react-core';

if (isInsufficientGoogleDriveScopesError(lastBackupError)) {
  // Re-prompt Google consent and retry.
}
For a turnkey, two-layer (pre-flight + post-flight) integration, see useGoogleDriveBackupReadiness.

Usage

import { useWalletBackup } from '@dynamic-labs/sdk-react-core';

const MyComponent = () => {
  const {
    initBackupProcess,
    areAllWalletsBackedUp,
    isGoogleLinked,
    backupState
  } = useWalletBackup();

  const handleBackup = () => {
    initBackupProcess();
  };

  return (
    <div>
      {!areAllWalletsBackedUp && (
        <div>
          <p>Your wallets are not backed up</p>
          {!isGoogleLinked && (
            <p>You'll need to link your Google account</p>
          )}
          <button onClick={handleBackup}>
            Back Up Wallets
          </button>
        </div>
      )}

      {backupState.isProcessing && (
        <p>Backing up wallet {backupState.currentIndex} of {backupState.totalWallets}</p>
      )}

      {backupState.isComplete && (
        <p>All wallets backed up successfully!</p>
      )}

      {backupState.hasError && (
        <p>Backup failed. Please try again.</p>
      )}
    </div>
  );
};

Advanced Usage

Monitor backup progress and handle errors:
import { useWalletBackup } from '@dynamic-labs/sdk-react-core';
import { useEffect } from 'react';

const BackupMonitor = () => {
  const {
    startBackup,
    backupState,
    getWalletsToBackup
  } = useWalletBackup();

  const handleBackupWithRetry = async () => {
    const walletsToBackup = getWalletsToBackup();

    if (walletsToBackup.length === 0) {
      console.log('No wallets need backup');
      return;
    }

    await startBackup(() => {
      console.log('Backup completed successfully');
    });
  };

  const handleRetry = async () => {
    if (backupState.failedIndex !== null) {
      await startBackup(
        () => console.log('Retry successful'),
        backupState.failedIndex
      );
    }
  };

  useEffect(() => {
    if (backupState.hasError) {
      console.error(`Backup failed at wallet ${backupState.failedIndex}`);
    }
  }, [backupState.hasError]);

  return (
    <div>
      <button onClick={handleBackupWithRetry}>
        Start Backup
      </button>

      {backupState.hasError && (
        <button onClick={handleRetry}>
          Retry Failed Backup
        </button>
      )}

      {backupState.isProcessing && (
        <div>
          <progress
            value={backupState.currentIndex}
            max={backupState.totalWallets}
          />
          <p>
            Backing up wallet {backupState.currentIndex} of {backupState.totalWallets}
          </p>
        </div>
      )}
    </div>
  );
};

Important Notes

  • The user must be authenticated to use backup functionality
  • Google account linking is required before backing up wallets
  • Backup operations are performed sequentially, one wallet at a time
  • The backup modal is part of the Dynamic UI and handles user interactions
  • Failed backups can be retried from the point of failure using the failedIndex

Learn More

Return Value

The hook returns an object with the following properties:
{
  areAllWalletsBackedUp: boolean;
  backupAllWallets: (wallets?: WalletToBackup[]) => Promise<void>;
  backupState: WalletOperationState;
  backupWallet: (walletToBackup: WalletToBackup) => Promise<boolean>;
  clearBackupError: () => void;
  ensureGoogleLinked: () => Promise<boolean>;
  getWalletsBackupStatus: () => WalletWithBackupStatus[];
  getWalletsToBackup: () => WalletToBackup[];
  initBackupProcess: () => void;
  isGoogleLinked: boolean;
  lastBackupError: unknown;
  startBackup: (onComplete?: () => void, fromIndex?: number) => Promise<void>;
}

Types

type WalletBackupStatus = 'backed-up' | 'pending';

interface WalletToBackup {
  address: string;
  chain: ChainEnum;
}

interface WalletWithBackupStatus extends WalletToBackup {
  status: WalletBackupStatus;
}

interface WalletOperationState {
  currentIndex: number;
  failedIndex: number | null;
  hasError: boolean;
  isComplete: boolean;
  isProcessing: boolean;
  totalWallets: number;
}