npm package: @subwallet/ui-keyring
Description: Basic packages of SubWallet
Author: AnhMTV
Latest version: 0.1.14

# @subwallet/ui-keyring

A comprehensive UI layer for managing cryptographic keypairs with persistent storage, reactive observables, and advanced keyring management features. This library provides a high-level interface over `@subwallet/keyring` with browser/Node.js storage capabilities and reactive state management.

## Overview

The `@subwallet/ui-keyring` serves as the bridge between the low-level cryptographic operations of `@subwallet/keyring` and user interface applications. It provides persistent storage, reactive observables for state management, and a comprehensive API for account lifecycle management.

## Architecture Overview

```mermaid
%%{init: {"theme": "dark"}}%%
graph LR
    A["@subwallet/ui-keyring"] --> B["Storage Layer"]
    A --> C["Observable Layer"]
    A --> D["Keyring Management"]
    A --> E["Options & Filtering"]

    %% Storage Layer
    B --> F["BrowserStore<br/>localStorage/sessionStorage"]
    B --> G["FileStore<br/>File System (Node.js)"]
    B --> H["PasswordStore<br/>Master Password Storage"]
    B --> I["Custom Store<br/>Database/Cloud Storage"]

    %% Observable Layer
    C --> J["Accounts Observable<br/>RxJS BehaviorSubject"]
    C --> K["Addresses Observable<br/>Watch-only Accounts"]
    C --> L["Contracts Observable<br/>Smart Contracts"]
    C --> M["Combined Observable<br/>All Entities"]

    %% Keyring Management
    D --> N["Account Creation<br/>Mnemonic/Private Key"]
    D --> O["Account Restoration<br/>JSON/Backup Files"]
    D --> P["Master Password<br/>Encryption/Decryption"]
    D --> Q["Multi-Chain Support<br/>Substrate/EVM/Bitcoin/TON"]

    %% Options & Filtering
    E --> R["Filtered Options<br/>Development/Production"]
    E --> S["Grouped Options<br/>Accounts/Addresses/Contracts"]
    E --> T["Recent Addresses<br/>Transaction History"]
    E --> U["Testing Mode<br/>Development Accounts"]

    %% Core Keyring Connection
    D --> V["@subwallet/keyring<br/>Core Cryptographic Operations"]
    V --> W["sr25519/ed25519/ecdsa<br/>Substrate Keypairs"]
    V --> X["ethereum<br/>EVM Keypairs"]
    V --> Y["bitcoin-*/bittest-*<br/>Bitcoin Keypairs"]
    V --> Z["ton/ton-native<br/>TON Keypairs"]
    V --> AA["cardano<br/>Cardano Keypairs"]

    %% Styling
    classDef storage fill:#1a237e,stroke:#3f51b5,stroke-width:2px
    classDef observable fill:#2e7d32,stroke:#4caf50,stroke-width:2px
    classDef management fill:#e65100,stroke:#ff9800,stroke-width:2px
    classDef options fill:#4a148c,stroke:#9c27b0,stroke-width:2px
    classDef keyring fill:#b71c1c,stroke:#f44336,stroke-width:2px

    class B,F,G,H,I storage
    class C,J,K,L,M observable
    class D,N,O,P,Q management
    class E,R,S,T,U options
    class V,W,X,Y,Z,AA keyring
```

## Core Concepts

### 1. **Storage Abstraction**
The ui-keyring provides pluggable storage backends:
- **BrowserStore**: Uses browser localStorage for web applications
- **FileStore**: Uses file system for Node.js applications
- **PasswordStore**: Manages master password storage
- **Custom Store**: Allows integration with databases or cloud storage

### 2. **Observable State Management**
All keyring state is managed through RxJS observables:
- **Reactive Updates**: UI components automatically update when accounts change
- **Filtered Views**: Different views for accounts, addresses, and contracts
- **Combined Observables**: Unified state management across all entity types

### 3. **Account Lifecycle Management**
Complete account management from creation to deletion:
- **Creation**: From mnemonic, private key, or hardware wallet
- **Storage**: Encrypted storage with master password support
- **Restoration**: From backup files or browser storage
- **Migration**: Seamless upgrades between keyring versions

## Main UI Keyring Workflow

```mermaid
%%{init: {"theme": "dark"}}%%
flowchart TD
    A["Application Start"] --> B["Initialize UI Keyring"]
    B --> C["Load Storage Options"]
    C --> D["Restore from Storage"]
    D --> E["Setup Observables"]
    E --> F["UI Ready"]

    %% Account Creation Flow
    F --> G["User Action:<br/>Create Account"]
    G --> H["Generate Mnemonic<br/>or Import Key"]
    H --> I["Create KeyringPair<br/>via @subwallet/keyring"]
    I --> J["Encrypt with<br/>Master Password"]
    J --> K["Save to Store"]
    K --> L["Update Observable"]
    L --> M["UI Updates<br/>Automatically"]

    %% Account Restoration Flow
    F --> N["User Action:<br/>Restore Accounts"]
    N --> O["Load JSON Backup"]
    O --> P["Decrypt with Password"]
    P --> Q["Validate Account Data"]
    Q --> R["Restore KeyringPairs"]
    R --> S["Update Storage"]
    S --> T["Update Observables"]
    T --> U["UI Reflects Changes"]

    %% Runtime Operations
    F --> V["Runtime Operations"]
    V --> W["Sign Transactions"]
    V --> X["Derive Accounts"]
    V --> Y["Manage Addresses"]
    V --> Z["Export Backups"]

    %% Storage Persistence
    K --> AA["Storage Layer"]
    S --> AA
    AA --> BB["BrowserStore<br/>localStorage"]
    AA --> CC["FileStore<br/>File System"]
    AA --> DD["Custom Store<br/>Database"]

    %% Observable Updates
    L --> EE["Observable Layer"]
    T --> EE
    EE --> FF["Accounts Subject"]
    EE --> GG["Addresses Subject"]
    EE --> HH["Contracts Subject"]
    EE --> II["Combined Observable"]

    %% UI Components
    M --> JJ["UI Components"]
    U --> JJ
    JJ --> KK["Account List"]
    JJ --> LL["Transaction Forms"]
    JJ --> MM["Settings Panel"]

    %% Styling
    classDef start fill:#1b5e20,stroke:#4caf50,stroke-width:2px
    classDef action fill:#0d47a1,stroke:#2196f3,stroke-width:2px
    classDef crypto fill:#e65100,stroke:#ff9800,stroke-width:2px
    classDef storage fill:#4a148c,stroke:#9c27b0,stroke-width:2px
    classDef ui fill:#b71c1c,stroke:#f44336,stroke-width:2px

    class A,B,C,D,E,F start
    class G,H,N,O,V,W,X,Y,Z action
    class I,J,P,Q,R crypto
    class K,S,AA,BB,CC,DD storage
    class L,T,EE,FF,GG,HH,II,M,U,JJ,KK,LL,MM ui
```

## Real-World Usage Examples

### 1. Initialize UI Keyring

```typescript
import { keyring } from '@subwallet/ui-keyring';
import { BrowserStore, PasswordBrowserStore } from '@subwallet/ui-keyring/stores';

// Initialize with browser storage
keyring.loadAll({
  type: 'sr25519', // Default keyring type
  isDevelopment: false,
  store: new BrowserStore(),
  password_store: new PasswordBrowserStore(),
  genesisHash: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3' // Polkadot
});

// Setup master password
keyring.changeMasterPassword('your-secure-master-password');
```

### 2. Create New Account

```typescript
import { keyring } from '@subwallet/ui-keyring';
import { mnemonicGenerate } from '@polkadot/util-crypto';

// Generate new mnemonic
const mnemonic = mnemonicGenerate();

// Create account with master password encryption
const { pair, json } = keyring.addUri(mnemonic, {
  name: 'My New Account',
  whenCreated: Date.now()
});

console.log('Account created:', pair.address);
console.log('Encrypted JSON:', json);

// Account is automatically saved to storage and observable is updated
```

### 3. Restore Accounts from Storage

```typescript
import { keyring } from '@subwallet/ui-keyring';

// This happens automatically when keyring.loadAll() is called
// But you can also restore from backup files

// Restore from JSON backup
const backupJson = {
  /* encrypted backup data */
};

keyring.restoreAccounts(backupJson, 'backup-password', [
  /* optional: specific addresses to restore */
]);

// Or restore single account
const accountJson = {
  /* single account JSON */
};

const restoredPair = keyring.restoreAccount(
  accountJson,
  'account-password',
  true // use master password
);
```

### 4. Subscribe to Account Changes

```typescript
import { keyring } from '@subwallet/ui-keyring';

// Subscribe to all accounts
keyring.accounts.subject.subscribe((accounts) => {
  console.log('Accounts updated:', Object.keys(accounts));
  
  // Update UI components
  updateAccountList(accounts);
});

// Subscribe to filtered options for dropdowns
keyring.keyringOption.optionsSubject.subscribe((options) => {
  console.log('Account options:', options.account);
  console.log('Address options:', options.address);
  console.log('Contract options:', options.contract);
  
  // Update UI dropdowns
  updateAccountDropdown(options.account);
});
```

### 5. Export Account Backups

```typescript
// Backup single account
const pair = keyring.getPair('account-address');
const accountBackup = keyring.backupAccount(pair, 'export-password');

// Backup all accounts
const allAccountsBackup = await keyring.backupAccounts(
  'master-password',
  ['address1', 'address2'] // optional: specific addresses
);

// Save to file or provide download
const backupBlob = new Blob([JSON.stringify(allAccountsBackup)], {
  type: 'application/json'
});
```

## Advanced Features

### 1. Custom Store Implementation

Create a custom store for database or cloud storage:

```typescript
import { KeyringStore, KeyringJson } from '@subwallet/ui-keyring/types';

export class DatabaseStore implements KeyringStore {
  private db: Database;

  constructor(database: Database) {
    this.db = database;
  }

  public all(fn: (key: string, value: KeyringJson) => void): void {
    this.db.query('SELECT key, value FROM keyring_accounts')
      .then(rows => {
        rows.forEach(row => {
          fn(row.key, JSON.parse(row.value));
        });
      });
  }

  public get(key: string, fn: (value: KeyringJson) => void): void {
    this.db.query('SELECT value FROM keyring_accounts WHERE key = ?', [key])
      .then(result => {
        if (result.length > 0) {
          fn(JSON.parse(result[0].value));
        }
      });
  }

  public set(key: string, value: KeyringJson, fn?: () => void): void {
    this.db.query(
      'INSERT OR REPLACE INTO keyring_accounts (key, value) VALUES (?, ?)',
      [key, JSON.stringify(value)]
    ).then(() => {
      fn && fn();
    });
  }

  public remove(key: string, fn?: () => void): void {
    this.db.query('DELETE FROM keyring_accounts WHERE key = ?', [key])
      .then(() => {
        fn && fn();
      });
  }
}

// Use custom store
keyring.loadAll({
  type: 'sr25519',
  store: new DatabaseStore(myDatabase),
  password_store: new DatabasePasswordStore(myDatabase)
});
```

### 2. Multi-Chain Account Management

```typescript
// Create accounts for different networks
const substrateAccount = keyring.addUri(mnemonic, { name: 'Substrate Account' }, 'sr25519');
const ethereumAccount = keyring.addUri(mnemonic, { name: 'Ethereum Account' }, 'ethereum');
const bitcoinAccount = keyring.addUri(mnemonic, { name: 'Bitcoin Account' }, 'bitcoin-84');

// Filter accounts by network
const accounts = keyring.getAccounts();
const substrateAccounts = accounts.filter(acc => ['sr25519', 'ed25519', 'ecdsa'].includes(acc.type));
const ethereumAccounts = accounts.filter(acc => acc.type === 'ethereum');
const bitcoinAccounts = accounts.filter(acc => acc.type?.startsWith('bitcoin'));
```

### 3. Development Mode and Testing

```typescript
// Enable development mode
keyring.setDevMode(true);

// Create test accounts (only visible in development)
const testAccount = keyring.addUri('//Alice', {
  name: 'Alice (Development)',
  isTesting: true
});

// Test accounts are automatically filtered in production
keyring.setDevMode(false);
const productionAccounts = keyring.getAccounts(); // testAccount not included
```

### 4. Address Management

```typescript
// Save frequently used addresses
keyring.saveAddress('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', {
  name: 'Polkadot Treasury',
  isRecent: false
});

// Mark recent addresses (automatically expire after 24 hours)
keyring.saveRecent('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');

// Manage smart contracts
keyring.saveContract('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', {
  name: 'My Smart Contract',
  contract: {
    abi: 'contract-abi-json',
    genesisHash: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3'
  }
});
```

## Integration with UI Frameworks

### React Integration

```typescript
import { useEffect, useState } from 'react';
import { keyring } from '@subwallet/ui-keyring';

function useKeyringAccounts() {
  const [accounts, setAccounts] = useState({});
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const subscription = keyring.accounts.subject.subscribe((accounts) => {
      setAccounts(accounts);
      setLoading(false);
    });

    return () => subscription.unsubscribe();
  }, []);

  return { accounts, loading };
}

// Usage in component
function AccountList() {
  const { accounts, loading } = useKeyringAccounts();

  if (loading) return <div>Loading accounts...</div>;

  return (
    <div>
      {Object.values(accounts).map(account => (
        <div key={account.json.address}>
          {account.json.meta.name} - {account.json.address}
        </div>
      ))}
    </div>
  );
}
```

### Vue Integration

```typescript
import { ref, onMounted, onUnmounted } from 'vue';
import { keyring } from '@subwallet/ui-keyring';

export function useKeyringAccounts() {
  const accounts = ref({});
  const loading = ref(true);
  let subscription = null;

  onMounted(() => {
    subscription = keyring.accounts.subject.subscribe((accountsData) => {
      accounts.value = accountsData;
      loading.value = false;
    });
  });

  onUnmounted(() => {
    if (subscription) {
      subscription.unsubscribe();
    }
  });

  return { accounts, loading };
}
```

## API Reference

### Core Methods

```typescript
// Account Management
keyring.addUri(suri: string, meta?: KeyringPair$Meta, type?: KeypairType): CreateResult
keyring.addExternal(address: string, meta?: KeyringPair$Meta): CreateResult
keyring.addPair(pair: KeyringPair, withMasterPassword: boolean, password?: string): CreateResult

// Backup & Restore
keyring.backupAccount(pair: KeyringPair, password: string): KeyringPair$Json
keyring.backupAccounts(password: string, addresses?: string[]): Promise<KeyringPairs$Json>
keyring.restoreAccount(json: KeyringPair$Json, password: string, withMasterPassword: boolean): KeyringPair
keyring.restoreAccounts(json: EncryptedJson, password: string, addresses?: string[]): void

// Master Password
keyring.changeMasterPassword(newPassphrase: string, oldPassphrase?: string): void
keyring.unlockKeyring(password: string): void
keyring.lockAll(isLockKering?: boolean): void

// Account Queries
keyring.getAccounts(): KeyringAddress[]
keyring.getAccount(address: string): KeyringAddress | undefined
keyring.getPair(address: string): KeyringPair
keyring.isAvailable(address: string): boolean

// Storage Management
keyring.loadAll(options: KeyringOptions): void
keyring.resetWallet(resetAll: boolean): void
```

### Observable Subjects

```typescript
// Account observables
keyring.accounts.subject: BehaviorSubject<SubjectInfo>
keyring.addresses.subject: BehaviorSubject<SubjectInfo>
keyring.contracts.subject: BehaviorSubject<SubjectInfo>

// Options observable
keyring.keyringOption.optionsSubject: BehaviorSubject<KeyringOptions>
```

## Installation

```bash
npm install @subwallet/ui-keyring
```

## Dependencies

- **@subwallet/keyring**: Core cryptographic operations
- **@polkadot/ui-settings**: Network configuration
- **@polkadot/util & @polkadot/util-crypto**: Utilities and cryptography
- **rxjs**: Reactive programming with observables
- **store**: Browser storage abstraction

## Security Best Practices

1. **Master Password**: Always use a strong master password for production applications
2. **Storage Security**: Consider using encrypted storage for sensitive data
3. **Memory Management**: Pairs are automatically locked when not in use
4. **Network Isolation**: Different networks are isolated in storage
5. **Development Mode**: Never enable development mode in production

## Contributing

Contributions are welcome! Please ensure:
- All tests pass
- New features include comprehensive tests
- Documentation is updated
- Security implications are considered
- Observable patterns are maintained

## License

Apache-2.0