npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@subwallet/ui-keyring

v0.1.14

Published

Basic packages of SubWallet

Readme

@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

%%{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

%%{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

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

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

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

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

// 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:

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

// 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

// 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

// 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

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

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

// 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

// 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

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