@empe/wallet-react-native

0.7.0 • Public • Published

@empe/wallet-react-native

Table of Contents

  1. Introduction
  2. Installation
  3. Basic Configuration
  4. Adapters in wallet-react-native
  5. WalletCoreProvider Context and useWalletCore Hook
  6. Creating and Managing DIDs
  7. Backup and Restore Process
  8. Handling Verifiable Credentials
  9. Example Implementation
  10. Extending and Customizing
  11. Support and Contribution

Introduction

@empe/wallet-react-native is a toolkit for managing DIDs (Decentralized Identifiers), keys, and credentials (Verifiable Credentials) in React Native applications. It is based on the @empe/wallet-core library and provides several adapters for storing:

  • DID Documents (RnDidDocumentStorageAdapter) in AsyncStorage.
  • Verifiable Credentials (RnVerifiableCredentialStorageAdapter) in AsyncStorage.
  • Seeds (RnNativeSeedStorageAdapter) via a native module (Android/iOS).
  • Keys (RnNativeSecretStorageAdapter) via a native module (Android/iOS).

Additionally, the SDK provides convenient React contexts: WalletCoreProvider for general wallet functionality and BackupProvider for easy backup management.

Installation

npm install @empe/wallet-react-native
# or
yarn add @empe/wallet-react-native

Make sure your React Native project is set up to use the following dependencies:

  • @react-native-async-storage/async-storage
  • react-native-fs (for backup functionality)
  • react-native-get-random-values (required for cryptographic operations)

Basic Configuration

To start using the SDK, wrap your application with WalletCoreProvider and optionally BackupProvider (if backup functionality is needed):

// App.tsx
import React from 'react';
import { WalletCoreProvider, BackupProvider } from '@empe/wallet-react-native';

export default function App() {
  return (
    <WalletCoreProvider
      config={{
        network: 'testnet', // or 'mainnet'
        // Optionally override default adapters:
        // didDocumentStorage: new RnDidDocumentStorageAdapter(),
        // verifiableCredentialStorage: new RnVerifiableCredentialStorageAdapter(),
        // seedStorage: new RnNativeSeedStorageAdapter(),
        // secretStorage: new RnNativeSecretStorageAdapter(),
      }}
    >
      <BackupProvider>
        <Main />
      </BackupProvider>
    </WalletCoreProvider>
  );
}

Adapters in wallet-react-native

The SDK provides the following adapters:

1. RnDidDocumentStorageAdapter

Stores DID documents in AsyncStorage.

import { RnDidDocumentStorageAdapter } from '@empe/wallet-react-native';

const didStorage = new RnDidDocumentStorageAdapter();

2. RnVerifiableCredentialStorageAdapter

Stores Verifiable Credentials in AsyncStorage. It can also fetch credential schemas (from credentialSchema.id) using axios.

import { RnVerifiableCredentialStorageAdapter } from '@empe/wallet-react-native';

const vcStorage = new RnVerifiableCredentialStorageAdapter();

3. RnNativeSeedStorageAdapter

Stores seeds in a native module. It requires the following methods to be defined in NativeModules.SeedModule:

saveSeed(did: string, seed: string): Promise<boolean>
getSeed(did: string): Promise<string>
deleteSeed(did: string): Promise<boolean>

4. RnNativeSecretStorageAdapter

Stores private and public keys in a native module KeysModule:

saveKeys(did: string, prv: string, pub: string, type: JwkCrv): Promise<boolean>
getKeys(did: string): Promise<{ prv: string; pub: string; type: JwkCrv }>
deleteKeys(did: string): Promise<boolean>

WalletCoreProvider Context and useWalletCore Hook

The main integration point is the WalletCoreProvider context. It provides:

  • Creation of a WalletCore instance with default (or custom) adapters passed in the config.
  • Exposure of that instance (walletCore) throughout your component tree via the useWalletCore hook.
  • Maintenance of a flow state for the currently initialized flow (Issuer or Verifier).
  • Providing the initializeFlow(url: string) method, which automatically detects whether the QR/URL is for an Issuer flow or a Verifier flow.
import { useWalletCore } from '@empe/wallet-react-native';

function MyComponent() {
  const { walletCore, flow, initializeFlow, resetFlow } = useWalletCore();

  // Now you can use walletCore to manage DIDs
  // ...
}

Creating and Managing DIDs

To create a new DID:

import { useWalletCore } from '@empe/wallet-react-native';

function CreateDidScreen() {
  const { walletCore } = useWalletCore();

  const createDid = async () => {
    const { seed } = walletCore.constructor.generateSeed();
    const doc = await walletCore.createDidDocument(seed);
    console.log('Created DID:', doc.id().toString());
  };

  return <Button title="Create DID" onPress={createDid} />;
}

Backup and Restore Process

BackupProvider and useBackup Hook

The SDK provides the BackupProvider context and useBackup hook for managing wallet backups:

import { BackupProvider, useBackup } from '@empe/wallet-react-native';

// In your main component
<BackupProvider>
  <YourApp />
</BackupProvider>;

// In child components
function BackupComponent() {
  const {
    createBackup,
    restoreFromMnemonic,
    restoreFromFile,
    isCreatingBackup,
    isRestoringBackup,
    lastBackupDate,
    backupError,
  } = useBackup();

  // Use these functions to manage backups
}

Backup Methods

The useBackup hook provides the following methods and state variables:

createBackup(customFileName?: string): Promise<string>

Creates a backup of the wallet's current state, including DID documents and verifiable credentials. Encryption is performed using core cryptographic functions from @empe/wallet-core.

  • Parameters:

    • customFileName (optional): A custom file name for the backup. If not provided, a default name with timestamp will be used: wallet_backup_${Date.now()}
  • Returns: A promise that resolves to the full path of the saved backup file

  • State Variables:

    • isCreatingBackup: Boolean indicating if a backup is currently being created
    • lastBackupDate: Date object of when the last backup was created
    • backupError: Error object if the last backup attempt failed

Example usage:

const handleCreateBackup = async () => {
  try {
    const fileName = `wallet_backup_${Date.now()}`;
    const backupPath = await createBackup(fileName);

    // Optional: Share the backup file
    const filePath =
      Platform.OS === 'android' ? 'file://' + backupPath : backupPath;
    await Share.share({
      url: filePath,
      title: 'Wallet Backup',
    });
  } catch (error) {
    console.error('Error creating backup:', error);
  }
};

Restore Methods

restoreFromMnemonic(mnemonic: string): Promise<BackupPayload | null>

Restores a wallet from a mnemonic phrase. This method attempts to derive the appropriate keys from the mnemonic and reconstruct the wallet state.

  • Parameters:
    • mnemonic: The mnemonic phrase to use for restoration
  • Returns: A promise that resolves to a BackupPayload object containing the restored wallet data, or null if no data could be restored

Example usage:

const handleRestoreFromMnemonic = async (mnemonic: string) => {
  try {
    const result = await restoreFromMnemonic(mnemonic.trim());

    if (result) {
      const vcCount = result.verifiableCredentials?.length || 0;
      console.log(
        `Restored DID document${vcCount > 0 ? ` and ${vcCount} credential(s)` : ''}`
      );
    }
  } catch (error) {
    console.error('Error restoring from mnemonic:', error);
  }
};

restoreFromFile(fileName: string, mnemonic: string): Promise<BackupPayload>

Restores a wallet from a backup file using the provided mnemonic to decrypt the data.

  • Parameters:

    • fileName: Path to the backup file
    • mnemonic: The mnemonic phrase to decrypt the backup file
  • Returns: A promise that resolves to a BackupPayload object containing the restored wallet data

  • State Variables:

    • isRestoringBackup: Boolean indicating if a restore operation is in progress
    • backupError: Error object if the last restore attempt failed

Example usage:

import DocumentPicker from 'react-native-document-picker';

// First, select a backup file
const handlePickBackupFile = async () => {
  try {
    const result = await DocumentPicker.pick({
      type: [DocumentPicker.types.allFiles],
    });
    return result[0].uri;
  } catch (err) {
    if (!DocumentPicker.isCancel(err)) {
      console.error('File picker error:', err);
    }
    return null;
  }
};

// Then restore from the selected file
const handleRestoreFromFile = async (filePath: string, mnemonic: string) => {
  try {
    const result = await restoreFromFile(filePath, mnemonic.trim());

    if (result) {
      const vcCount = result.verifiableCredentials?.length || 0;
      console.log(
        `Restored DID document${vcCount > 0 ? ` and ${vcCount} credential(s)` : ''}`
      );
    }
  } catch (error) {
    console.error('Error restoring from file:', error);
  }
};

Technical Details

The backup functionality uses:

  1. OffChainBackupStorage - Implements the IBackupStorage interface from @empe/wallet-core to handle file operations via React Native's RNFS module.

  2. BackupFlowManager - Manages the backup and restore processes, coordinating between different storage adapters.

  3. Secp256k1 - Provides cryptographic functions for securing the backup data.

Handling Verifiable Credentials

The SDK allows requesting, storing, and presenting verifiable credentials:

import { useWalletCore } from '@empe/wallet-react-native';

function CredentialScreen() {
  const { walletCore, flow, initializeFlow } = useWalletCore();

  // Initialize flow from QR code
  const handleScanQr = async (qrUrl: string) => {
    try {
      const { flow: detectedFlow, type } = await initializeFlow(qrUrl);

      if (type === 'claim') {
        // Issuer Flow (receiving credential)
        await detectedFlow.process({
          confirmCredentialClaim: async (offering) => {
            console.log('Receiving credential from:', offering.credential_issuer);
            return true; // Confirm the user wants to receive this credential
          },
        });
      } else if (type === 'presentation') {
        // Verifier Flow (presenting credential)
        await detectedFlow.process({
          onNoMatchingCredentials: async () => {
            console.log('No matching credentials found');
          },
          onUserConsent: async (matchedVCs) => {
            // Return e.g. all matched VCs
            return matchedVCs;
          },
        });
      }
    } catch (err) {
      console.error('Flow error:', err);
    }
  };

  return (
    // UI for scanning QR codes, etc.
  );
}

Example Implementation

Full example of an application managing DIDs, credentials, and backups:

import React, { useState, useEffect } from 'react';
import { View, Text, Button, TextInput, Alert } from 'react-native';
import { useWalletCore, useBackup } from '@empe/wallet-react-native';
import DocumentPicker from 'react-native-document-picker';

export function WalletScreen() {
  const { walletCore } = useWalletCore();
  const { createBackup, restoreFromMnemonic, restoreFromFile } = useBackup();

  const [didId, setDidId] = useState('');
  const [mnemonic, setMnemonic] = useState('');
  const [backupPath, setBackupPath] = useState('');

  // Check for existing DID on start
  useEffect(() => {
    checkExistingDid();
  }, []);

  const checkExistingDid = async () => {
    try {
      const docs = await walletCore.didDocumentStorage.getAll();
      if (docs && docs.length > 0) {
        setDidId(docs[0].id().toString());
      }
    } catch (error) {
      console.error('Error checking for existing DID:', error);
    }
  };

  const createDid = async () => {
    try {
      const { seed } = walletCore.constructor.generateSeed();
      const doc = await walletCore.createDidDocument(seed);
      setDidId(doc.id().toString());
      Alert.alert('Success', `Created DID: ${doc.id().toString()}`);
    } catch (error) {
      Alert.alert('Error', `Failed to create DID: ${error.message}`);
    }
  };

  const handleCreateBackup = async () => {
    if (!didId) {
      Alert.alert('Error', 'Please create a DID first');
      return;
    }

    try {
      const fileName = `wallet_backup_${Date.now()}`;
      const path = await createBackup(fileName);
      setBackupPath(path);
      Alert.alert('Success', `Created backup: ${path}`);
    } catch (error) {
      Alert.alert('Error', `Failed to create backup: ${error.message}`);
    }
  };

  const handleRestoreFromMnemonic = async () => {
    try {
      const result = await restoreFromMnemonic(mnemonic);
      if (result) {
        await checkExistingDid();
        Alert.alert('Success', 'Wallet restored successfully');
      }
    } catch (error) {
      Alert.alert('Error', `Failed to restore from mnemonic: ${error.message}`);
    }
  };

  const handleSelectBackupFile = async () => {
    try {
      const result = await DocumentPicker.pick({
        type: [DocumentPicker.types.allFiles],
      });
      setBackupPath(result[0].uri);
    } catch (error) {
      if (!DocumentPicker.isCancel(error)) {
        Alert.alert('Error', 'Failed to select file');
      }
    }
  };

  const handleRestoreFromFile = async () => {
    if (!backupPath || !mnemonic) {
      Alert.alert(
        'Error',
        'Please select a backup file and provide a mnemonic'
      );
      return;
    }

    try {
      const result = await restoreFromFile(backupPath, mnemonic);
      if (result) {
        await checkExistingDid();
        Alert.alert('Success', 'Wallet restored successfully');
      }
    } catch (error) {
      Alert.alert('Error', `Failed to restore from file: ${error.message}`);
    }
  };

  return (
    <View style={{ padding: 20 }}>
      {/* DID management UI */}
      {/* Backup creation UI */}
      {/* Restore from mnemonic UI */}
      {/* Restore from file UI */}
    </View>
  );
}

Extending and Customizing

The SDK can be extended and customized to your needs:

  • You can replace the default adapters with your own (e.g., to store data in a different system).
  • You can implement your own UI layer for identity management processes.
  • You can customize credential flows for your application's needs.

Example of a custom adapter:

import { IDidDocumentStorage } from '@empe/wallet-core';

class CustomDidDocumentStorage implements IDidDocumentStorage {
  // Implement the required methods
}

// Use in WalletCoreProvider
<WalletCoreProvider
  config={{
    network: 'testnet',
    didDocumentStorage: new CustomDidDocumentStorage(),
  }}
>
  {/* ... */}
</WalletCoreProvider>;

Support and Contribution

If you encounter issues with the SDK or have suggestions for improvements, use GitHub Issues or Pull Requests in the repository:


© 2023-2025 empe.io | MIT License

Package Sidebar

Install

npm i @empe/wallet-react-native

Weekly Downloads

23

Version

0.7.0

License

MIT

Unpacked Size

206 kB

Total Files

134

Last publish

Collaborators

  • empe