- Introduction
- Installation
- Basic Configuration
- Adapters in wallet-react-native
- WalletCoreProvider Context and useWalletCore Hook
- Creating and Managing DIDs
- Backup and Restore Process
- Handling Verifiable Credentials
- Example Implementation
- Extending and Customizing
- Support and Contribution
@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.
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)
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>
);
}
The SDK provides the following adapters:
Stores DID documents in AsyncStorage.
import { RnDidDocumentStorageAdapter } from '@empe/wallet-react-native';
const didStorage = new RnDidDocumentStorageAdapter();
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();
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>
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>
The main integration point is the WalletCoreProvider
context. It provides:
- Creation of a
WalletCore
instance with default (or custom) adapters passed in theconfig
. - Exposure of that instance (
walletCore
) throughout your component tree via theuseWalletCore
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
// ...
}
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} />;
}
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
}
The useBackup
hook provides the following methods and state variables:
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);
}
};
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);
}
};
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);
}
};
The backup functionality uses:
-
OffChainBackupStorage
- Implements theIBackupStorage
interface from@empe/wallet-core
to handle file operations via React Native'sRNFS
module. -
BackupFlowManager
- Manages the backup and restore processes, coordinating between different storage adapters. -
Secp256k1
- Provides cryptographic functions for securing the backup data.
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.
);
}
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>
);
}
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>;
If you encounter issues with the SDK or have suggestions for improvements, use GitHub Issues or Pull Requests in the repository:
- GitHub: empe-io/wallet-react-native
- Email: dev@empe.io
© 2023-2025 empe.io | MIT License