@empe/wallet-core
TypeScript icon, indicating that this package has built-in type declarations

3.1.0 • Public • Published

This guide explains how to get started with the @empe/wallet-core library for managing decentralized identity (DID, Verifiable Credentials, etc.) in a Node.js or any JavaScript/TypeScript environment (without React Native requirements).


Table of Contents

  1. Introduction
  2. Installation
  3. Basic Usage
  4. Creating a DID Document
  5. Issuer Flow (ClaimCredentialFlow)
  6. Verifier Flow (CredentialPresentationFlow)
  7. Backup and Restore Functionality
  8. APIs and Adapters
  9. Errors and Exception Handling
  10. Support and Contribution

Introduction

@empe/wallet-core is a TypeScript/JavaScript library that provides:

  • Creation and storage of DID Documents.
  • Generation/import of seeds (mnemonics).
  • Key and signing management (KeyStorage).
  • Handling Verifiable Credentials (VCs).
  • Ready-made flow classes for issuance and verification processes:
    • ClaimCredentialFlow – receiving a credential from an external issuer.
    • CredentialPresentationFlow – presenting credentials to a verifier.

The library simplifies integration with the DID ecosystem and W3C standards such as DIDs, VCs, and Verifiable Presentations.


Installation

npm install @empe/wallet-core
# or
yarn add @empe/wallet-core

Note: This library is not specific to React or React Native. You can use it in any JS/TS environment.


Basic Usage

Below is a sample showing how to configure WalletCore and create your first DID Document:

import {
    WalletCore,
    ClaimCredentialFlow, // Flow for receiving credentials
    CredentialPresentationFlow, // Flow for presenting credentials
} from '@empe/wallet-core';

import {
    IDidDocumentStorageAdapter,
    IKeyStorageAdapter,
    ISecretStorageAdapter,
    IVerifiableCredentialStorageAdapter,
} from '@empe/wallet-core';

/**
 * Sample in-memory adapters for testing purposes.
 * In production, replace these with a database, files, or another storage solution.
 */
class InMemoryDidDocumentStorage implements IDidDocumentStorageAdapter {
    private store = new Map<string, any>();
    async add(network: string, value: any) {
        this.store.set(network, value);
    }
    async get(network: string) {
        return this.store.get(network) ?? null;
    }
}

class InMemoryVCStorage implements IVerifiableCredentialStorageAdapter {
    private vcs = new Map<string, any>();
    async add(key: string, value: any) {
        this.vcs.set(key, value);
    }
    async getAll() {
        return [...this.vcs.values()];
    }
}

class InMemorySecretStorage implements ISecretStorageAdapter {
    private store = new Map<string, string>();
    async add(key: string, value: string) {
        this.store.set(key, value);
    }
    async get(key: string) {
        return this.store.get(key) ?? null;
    }
}

class InMemoryKeyStorage implements IKeyStorageAdapter {
    private store = new Map<string, any>();
    async add(k: string, v: any) {
        this.store.set(k, v);
    }
    async get(k: string) {
        return this.store.get(k) ?? null;
    }
    async getAll() {
        return [];
    }
    async has(k: string) {
        return this.store.has(k);
    }
    async remove(k: string) {
        this.store.delete(k);
    }
}

// Create instances of our adapters
const didDocumentStorage = new InMemoryDidDocumentStorage();
const vcStorage = new InMemoryVCStorage();
const seedStorage = new InMemorySecretStorage();
const keyStorage = new InMemoryKeyStorage();

// Initialize WalletCore
const walletCore = new WalletCore({
    network: 'testnet',
    didDocumentStorage,
    verifiableCredentialStorage: vcStorage,
    seedStorage,
    secretStorage: keyStorage,
});

// Example usage
(async () => {
    // 1) Generate a seed
    const { seed, mnemonics } = WalletCore.generateSeed();
    console.log('Generated seed:', seed);
    console.log('Mnemonic phrase:', mnemonics);

    // 2) Create a DID Document
    const didDoc = await walletCore.createDidDocument(seed);
    console.log('DID Document:', didDoc.id().toString());

    // 3) Retrieve the same DID Document
    const sameDoc = await walletCore.getDidDocument('testnet');
    console.log('Retrieved DID Document:', sameDoc.id().toString());
})();

Creating a DID Document

The createDidDocument(seed) method creates a new DID Document based on the provided seed (hex string). Internally, it uses an HDKeyFactory from @empe/identity to generate keys. Example:

const { seed } = WalletCore.generateSeed();
const document = await walletCore.createDidDocument(seed);
console.log('DID:', document.id().toString());

After creation, the document is stored using the didDocumentStorage adapter.


Issuer Flow (ClaimCredentialFlow)

ClaimCredentialFlow is used to receive a credential from an external issuer. Typically, you start by scanning a QR code or fetching issuer endpoint data.

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

// ...
const data = {
    credential_id: 'BasicCredential',
    credential_issuer: 'https://issuer.example.com',
    credential_configuration_ids: ['...'],
    display: { name: 'Example Issuer', locale: 'en-US', description: 'Sample' },
};

const claimFlow = new ClaimCredentialFlow({
    data,
    network: 'testnet',
    didDocumentStorage,
    verifiableCredentialStorage: vcStorage,
    secretStorage: keyStorage,
});

await claimFlow.process({
    confirmCredentialClaim: async offering => {
        // Show the user info about the credential (offering)
        // Wait for them to confirm "Yes, I want to claim it"
        console.log('Receiving credential from:', offering.credential_issuer);
    },
});

If the process succeeds, the new credential (VC) is stored in your verifiableCredentialStorage.


Verifier Flow (CredentialPresentationFlow)

CredentialPresentationFlow is used to present your stored credentials (VCs) to a verifier. It also typically starts with a QR code that provides verifier data.

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

const verifierData = {
    client_id: 'verifier.example.com',
    presentation_definition: {
        input_descriptors: [
            // Verifier requirements
        ],
    },
    state: 'abc123',
    nonce: 'xyz789',
    response_uri: 'https://verifier.example.com/submit',
    // ...
};

const presentationFlow = new CredentialPresentationFlow({
    qrData: verifierData,
    network: 'testnet',
    didDocumentStorage,
    verifiableCredentialStorage: vcStorage,
    secretStorage: keyStorage,
});

const success = await presentationFlow.process({
    onNoMatchingCredentials: async () => {
        console.log('No local VCs match the verifier requirements.');
    },
    onUserConsent: async matchedVCs => {
        // Prompt user to choose which of matchedVCs to present
        console.log('Found matched VCs:', matchedVCs);
        return matchedVCs; // present all of them
    },
});

if (success) {
    console.log('Successfully presented credentials to the verifier.');
}

Backup and Restore Functionality

BackupFlowManager enables users to securely backup and restore their wallet data (DID documents, verifiable credentials, and cryptographic keys). This functionality is crucial for wallet recovery and cross-device synchronization.

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

// Initialize the backup manager
const backupFlowManager = new BackupFlowManager(
    backupManager, // IBackupManager implementation
    backupStorage, // IBackupStorage implementation
    didDocumentStorage, // For storing DID documents
    vcStorage, // For storing verifiable credentials
    keyStorage, // For managing cryptographic keys
    secretStorage // For storing sensitive data
);

// Create and export a backup
async function createBackup() {
    const backupPath = await backupFlowManager.exportBackup(
        'my-wallet-backup.json', // Filename for the backup
        null, // Optional encryption key (uses wallet key if null)
        {
            // Optional callbacks for progress feedback
            onBackupStarted: () => {
                console.log('Backup process started');
            },
            onBackupProgress: progress => {
                console.log(`Backup progress: ${progress}%`);
            },
            onBackupCompleted: backupPath => {
                console.log(`Backup completed and saved to: ${backupPath}`);
            },
        }
    );

    return backupPath;
}

// Restore from backup file using mnemonic
async function restoreFromBackup(backupFilePath, mnemonic) {
    const restoredData = await backupFlowManager.importFromFile(backupFilePath, mnemonic, {
        // Optional callbacks for the restore process
        onImportStarted: () => {
            console.log('Import process started');
        },
        onImportProgress: progress => {
            console.log(`Import progress: ${progress}%`);
        },
        onImportCompleted: restoredData => {
            console.log('Import completed successfully', restoredData);
        },
        onImportFailed: error => {
            console.error('Import failed:', error);
        },
    });

    return restoredData;
}

// Restore using just a mnemonic (without a backup file)
async function restoreFromMnemonic(mnemonic) {
    const restoredData = await backupFlowManager.importFromMnemonic(mnemonic, {
        onBackupFileNotFound: async () => {
            // This callback is triggered when no backup file is found
            // You can prompt the user to provide a backup file path or return null
            // to continue without a backup file
            return null;
        },
        onInvalidMnemonic: async () => {
            // This callback is triggered when the provided mnemonic is invalid
            // You can prompt the user to provide a valid mnemonic or handle the error
            console.error('Invalid mnemonic provided');
            return await promptUserForValidMnemonic(); // Example function
        },
        onTypeDetected: didType => {
            console.log(`DID type detected: ${didType}`);
        },
        onRestoreOffChainWithoutBackup: async () => {
            // This callback is triggered when attempting to restore off-chain data without a backup file
            // Return true to continue with off-chain restoration, false to abort
            return await promptUserToConfirmOffChainRestore(); // Example function
        },
        onRestoreOnChainWithoutBackup: async () => {
            // This callback is triggered when attempting to restore on-chain data without a backup file
            // Return true to continue with on-chain restoration, false to abort
            return await promptUserToConfirmOnChainRestore(); // Example function
        },
    });

    return restoredData;
}

The BackupFlowCallbacks interface provides a rich set of hooks for managing the backup and restore user experience:

Callback Description
onBackupStarted Called when a backup process begins
onBackupProgress Called periodically with progress percentage during backup
onBackupCompleted Called when backup completes successfully with the backup file path
onImportStarted Called when a restore/import process begins
onBackupFileNotFound Called when no backup file is found during restore; should return a file path or null
onInvalidMnemonic Called when the provided mnemonic is invalid; should return a valid mnemonic or null
onTypeDetected Called when the DID type is detected during restore
onRestoreOffChainWithoutBackup Called when attempting off-chain restore without backup; should return boolean
onRestoreOnChainWithoutBackup Called when attempting on-chain restore without backup; should return boolean
onImportProgress Called periodically with progress percentage during import/restore
onImportCompleted Called when import/restore completes successfully with restored data
onImportFailed Called when import/restore fails with the error

APIs and Adapters

The library offers several interfaces for integrating with your storage system:

  • IDidDocumentStorageAdapter – manages storing a DID Document per network.
  • IVerifiableCredentialStorageAdapter – manages storing one or more VCs.
  • ISecretStorageAdapter – stores seeds, typically tied to the DID (or another key).
  • IKeyStorageAdapter – manages the keys (and includes signing logic).

In files like api.ts, issuer-flow.ts, and verifier-flow.ts, you’ll find functions and classes that handle the HTTP communication with issuers and verifiers.


Errors and Exception Handling

The library uses custom error classes (WalletCoreSDKError) for more descriptive exception handling (see error.ts). HTTP functions rely on toNormalizedError to convert errors (e.g., from Axios) into a consistent format.


Support and Contribution

If you have feedback, encounter bugs, or want to propose improvements to @empe/wallet-core, please open an issue or submit a pull request in the project repository. Every bit of help is appreciated!

Readme

Keywords

none

Package Sidebar

Install

npm i @empe/wallet-core

Weekly Downloads

4

Version

3.1.0

License

MIT

Unpacked Size

778 kB

Total Files

124

Last publish

Collaborators

  • empe