A TypeScript library for interfacing with Ledger hardware wallets to manage Mochimo cryptocurrency transactions. Supports both USB (WebHID) and Bluetooth (Web BLE) connections for web and mobile applications.
- 🔐 Secure Hardware Integration: Complete integration with Ledger hardware wallets
- 🌐 Cross-Platform: Works on web browsers and mobile apps (via Capacitor)
- 🔌 Multiple Connection Types: USB (WebHID) and Bluetooth (Web BLE) support
- 📱 Mobile-Optimized: Advanced device discovery and selection for mobile workflows
- ✅ WOTS+ Signatures: Full support for Mochimo's WOTS+ signature scheme
- 🛡️ Input Validation: Robust validation of Mochimo reference formats and transaction limits
- 📊 TypeScript: Full type safety and excellent developer experience
- 🧪 Well Tested: Comprehensive test suite with 42+ test cases
npm install ledger-mochimoEssential functions for Ledger Mochimo integration:
const transport = new MochimoTransport();
await transport.connectAuto(); // Auto-detects USB/Bluetoothconst app = new MochimoApp(transport);const address = await app.getAddress({ account: 0, addressIndex: 5 });const transaction = {
bip32Path: { account: 0, addressIndex: 5 },
destinations: [{ tag: addressHash, ref: 'PAYMENT', amount: 1000000000n }],
sourceTotal: 1500000000n,
feeTotal: 500000000n,
nonce: 123456789n,
blkToLive: 0n
};const signature = await app.signTransactionComplete(transaction);const devices = await MochimoTransport.discoverDevices();
await transport.connectToDevice(devices[0]);try {
await app.signTransactionComplete(transaction);
} catch (error) {
if (error instanceof TransactionRejected) { /* User denied */ }
}// ✅ Valid: 'ABC', '123', 'AB-12-CD'
// ❌ Invalid: 'abc', 'AB-CD', 'AB_12'const hasUSB = MochimoTransport.isWebHIDSupported();
const hasBluetooth = MochimoTransport.isWebBLESupported();await transport.disconnect();import { MochimoApp, MochimoTransport } from 'ledger-mochimo';
// Create transport and app
const transport = new MochimoTransport();
const app = new MochimoApp(transport);
// Connect to device (auto-detects best transport)
await transport.connectAuto();
// Generate address
const address = await app.getAddress({
account: 0,
addressIndex: 5
});
console.log('Address:', address.addressHash);import { MochimoApp, MochimoTransport } from 'ledger-mochimo';
// 1. Discover available devices
const devices = await MochimoTransport.discoverDevices();
console.log('Available devices:', devices);
// 2. Let user select device (implement your own UI)
const selectedDevice = devices[0]; // or show selection UI
// 3. Connect to selected device
const transport = new MochimoTransport();
await transport.connectToDevice(selectedDevice);
// 4. Create app instance
const app = new MochimoApp(transport);// Show native browser device selection dialog
const device = await MochimoTransport.requestBluetoothDevice({
allowAllDevices: false // Only show Ledger devices
});
if (device) {
const transport = new MochimoTransport();
await transport.connectToDevice(device);
}// Scan for devices with more control
const device = await MochimoTransport.discoverAndSelectBluetoothDevice({
scanTime: 15000, // 15 seconds
showUnknownDevices: false
});The library enforces Mochimo's transaction limits:
- Maximum 4 destinations per transaction (APDU limit)
- Maximum 255 bytes per APDU command
- Reference format validation (uppercase/digits/dashes only)
- Sorted destinations required by protocol
// ✅ Valid transaction (up to 4 destinations)
const transaction = {
bip32Path: { account: 0, addressIndex: 5 },
destinations: [
{ tag: address1, ref: 'PAY-123', amount: 1000000000n },
{ tag: address2, ref: 'FEE-456', amount: 2000000000n },
{ tag: address3, ref: 'SEND-789', amount: 3000000000n },
{ tag: address4, ref: 'CHANGE-1', amount: 4000000000n }
],
sourceTotal: 10200000000n,
feeTotal: 200000000n,
nonce: 123456789n,
blkToLive: 0n
};
// ❌ Invalid - too many destinations
const invalidTransaction = {
destinations: [dest1, dest2, dest3, dest4, dest5] // 5 destinations = error
};Mochimo references must follow specific rules:
// ✅ Valid references
'ABC' // Uppercase letters only
'123' // Digits only
'AB-12-CD' // Alternating groups
'XY-789-PQ' // Mixed with proper alternation
'' // Empty reference
// ❌ Invalid references
'AB-CD' // Consecutive letter groups
'12-34' // Consecutive digit groups
'abc' // Lowercase letters
'AB_12' // Invalid characters
'-ABC' // Starting with dash
'ABC-' // Ending with dashSee examples/enhanced-mobile-integration.ts for a complete mobile implementation:
import { MochimoMobileIntegration, ReactNativeMochimoUI } from 'ledger-mochimo/examples';
const ui = new ReactNativeMochimoUI();
const mochimo = new MochimoMobileIntegration(ui);
// Complete setup workflow with device selection
const connected = await mochimo.setupLedgerConnection();
if (connected) {
const address = await mochimo.generateAddress(0, 5);
const signature = await mochimo.signTransaction(transaction);
}Connection management and device discovery.
// Static methods
MochimoTransport.discoverDevices(options?)
MochimoTransport.requestBluetoothDevice(options?)
MochimoTransport.discoverAndSelectBluetoothDevice(options?)
MochimoTransport.isWebHIDSupported()
MochimoTransport.isWebBLESupported()
MochimoTransport.getSupportedTransports()
// Instance methods
transport.connectAuto(options?)
transport.connectWebHID(options?)
transport.connectWebBLE(options?)
transport.connectToDevice(device, options?)
transport.disconnect()
transport.getStatus()Main application interface for Ledger operations.
// Address generation
app.getAddress(path: MochimoBIP32Path): Promise<MochimoAddress>
// Transaction signing (two-phase process)
app.signTransaction(transaction: MochimoTransaction): Promise<ApprovalResponse>
app.downloadSignature(totalChunks: number): Promise<SignatureResponse>
// Complete signing workflow
app.signTransactionComplete(transaction: MochimoTransaction): Promise<MochimoSignature>
// Static discovery methods
MochimoApp.discoverDevices(options?)
MochimoApp.requestBluetoothDevice(options?)interface MochimoBIP32Path {
account: number; // Account number
addressIndex: number; // Address index
}
interface MochimoDestination {
tag: Uint8Array; // 20-byte address tag
ref: string; // Reference/memo (validated format)
amount: bigint; // Amount in nMCM
}
interface MochimoTransaction {
bip32Path: MochimoBIP32Path;
destinations: MochimoDestination[]; // Max 4 destinations
sourceTotal: bigint;
feeTotal: bigint;
nonce: bigint;
blkToLive: bigint; // Must be 0
}
interface LedgerDeviceInfo {
id: string;
name: string;
type: 'webhid' | 'webble';
connected: boolean;
bluetoothDevice?: any; // For Web BLE devices
}| Platform | WebHID (USB) | Web BLE (Bluetooth) |
|---|---|---|
| Chrome Desktop | ✅ | ✅ |
| Chrome Mobile | ❌ | ✅ |
| Safari Desktop | ❌ | ❌ |
| Safari Mobile | ❌ | ❌ |
| Capacitor iOS | ❌ | ✅ |
| Capacitor Android | ❌ | ✅ |
The library provides specific error types:
import {
MochimoLedgerError,
AddressReuseWarning,
TransactionRejected
} from 'ledger-mochimo';
try {
await app.signTransaction(transaction);
} catch (error) {
if (error instanceof TransactionRejected) {
console.log('User rejected transaction');
} else if (error instanceof AddressReuseWarning) {
console.log('Address reuse warning - user must confirm');
} else if (error instanceof MochimoLedgerError) {
console.log('Ledger error:', error.message);
}
}# Install dependencies
npm install
# Run tests
npm test
# Build library
npm run build
# Lint code
npm run lintThe library includes comprehensive tests:
- 42 test cases covering all functionality
- Reference format validation tests
- Transaction limit validation
- Device discovery simulation
- Error handling verification
npm testexamples/mobile-integration.ts- Basic mobile integrationexamples/enhanced-mobile-integration.ts- Advanced mobile workflow with device selection- Mobile UI implementation examples for React Native
m/44'/1027'/account'/0/address_index
- Purpose: 44 (BIP44)
- Coin Type: 1027 (Mochimo)
- Account: User-defined account number
- Change: Always 0 (external addresses)
- Address Index: Address index within account
Phase 1 - Address Generation (INS=0x05):
[CLA][INS][P1][P2][LC][BIP32_PATH]
Phase 2 - Transaction Signing (INS=0x06):
[CLA][INS][P1][P2][LC][BIP32_PATH][TRANSACTION_DATA]
Phase 3 - Signature Download (INS=0x07):
[CLA][INS][P1][P2][LC][CHUNK_INDEX]
- Signature length: 2144 bytes
- Downloaded in chunks (max 255 bytes per APDU)
- Requires 9+ chunks for complete signature
MIT License - see LICENSE file for details.
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
This library handles cryptocurrency transactions and private key operations. Always:
- Test thoroughly before production use
- Validate all user inputs
- Use hardware wallets for transaction signing
- Keep the library updated
- GitHub Issues: Report bugs and feature requests
- Documentation: Check examples and API reference
- Mochimo Community: Join the Mochimo Discord/Telegram
// Connect with auto-detection
await app.connect();
// Connect with specific transport
await app.connect('webhid'); // USB
await app.connect('webble'); // Bluetooth
// Disconnect
await app.disconnect();
// Check connection status
const status = app.getConnectionStatus();// Generate address silently
const address = await app.getAddress({
account: 0,
addressIndex: 5
}, false);
// Generate address with device verification
const verifiedAddress = await app.verifyAddress({
account: 0,
addressIndex: 5
});
// Get account tag (for wallet integration)
const accountTag = await app.getAccountTag(0);// Complete transaction signing workflow
const signature = await app.signTransactionComplete({
bip32Path: { account: 0, addressIndex: 5 },
destinations: [{
tag: destinationAddressHash, // 20-byte Uint8Array
ref: 'Payment memo', // Max 15 characters
amount: 1000000000n // Amount in nMCM (bigint)
}],
sourceTotal: 2000000000n, // Total available from UTXO
feeTotal: 500000000n, // Transaction fee
nonce: 123456789n, // Block number
blkToLive: 0n // Must be 0 for Mochimo
});
console.log('Signature:', signature.signature); // 2144-byte WOTS+ signature// Step-by-step signing for custom workflows
const approval = await app.signTransaction(transaction);
console.log('Transaction ID:', approval.transactionId);
const signatureData = await app.downloadSignature(
approval.sessionInfo.totalChunks
);import { MochimoTransport } from 'ledger-mochimo';
// Check supported transports
const supported = MochimoTransport.getSupportedTransports();
console.log('Supported:', supported); // ['webhid', 'webble']
// Manual transport management
const transport = new MochimoTransport();
await transport.connectWebHID(); // USB connection
await transport.connectWebBLE(); // Bluetooth connection
// Check device availability
const isAvailable = await MochimoTransport.isWebHIDDeviceAvailable();interface MochimoBIP32Path {
account: number; // Account number (hardened)
addressIndex: number; // Address index within account
}
interface MochimoDestination {
tag: Uint8Array; // 20-byte address hash
ref: string; // Reference/memo (max 15 chars)
amount: bigint; // Amount in nMCM
}
interface MochimoTransaction {
bip32Path: MochimoBIP32Path;
destinations: MochimoDestination[];
sourceTotal: bigint; // Total UTXO amount
feeTotal: bigint; // Transaction fee
nonce: bigint; // Block number
blkToLive: bigint; // Must be 0
}interface MochimoAddress {
addressHash: Uint8Array; // 20-byte address hash
addressIndex: number; // Address index used
pubSeed: Uint8Array; // 32-byte WOTS+ public seed
addrPartial: Uint8Array; // 20-byte partial addr
}
interface MochimoSignature {
signature: Uint8Array; // 2144-byte WOTS+ signature
transactionId: Uint8Array; // 32-byte transaction ID
pubSeed: Uint8Array; // 32-byte WOTS+ public seed
addr: Uint8Array; // 32-byte WOTS+ address
}Mochimo uses the path structure: m/44'/1027'/account'/0/address_index
44'- BIP44 purpose (hardened)1027'- Mochimo coin type (hardened)account'- Account number (hardened)0- Change index (always 0, non-hardened)address_index- Address index (non-hardened)
- Maximum destinations per APDU: 4 destinations
- Maximum APDU size: 255 bytes
- Signature size: 2144 bytes (WOTS+)
- Chunk download: 9 chunks of max 240 bytes each
- Account Tag: Generated with
address_index = 0, used for account identification - Address Hash: Generated with any
address_index, used for transactions
import {
MochimoLedgerError,
AddressReuseWarning,
TransactionRejected
} from 'ledger-mochimo';
try {
const signature = await app.signTransactionComplete(transaction);
} catch (error) {
if (error instanceof TransactionRejected) {
console.log('User rejected the transaction');
} else if (error instanceof AddressReuseWarning) {
console.log('Address reuse warning - user must confirm');
} else if (error instanceof MochimoLedgerError) {
console.log('Ledger error:', error.message, 'Code:', error.code);
}
}- ✅ Chrome/Chromium - Full WebHID and Web BLE support
- ✅ Edge - Full WebHID and Web BLE support
⚠️ Firefox - Limited Web BLE support- ❌ Safari - No WebHID/Web BLE support
- ✅ iOS - Bluetooth support via Web BLE
- ✅ Android - Bluetooth support via Web BLE
- ❌ USB connections - Not supported on mobile
// Check what's supported in current environment
const capabilities = app.getCapabilities();
console.log('Supported transports:', capabilities.supportedTransports);
// Check specific features
const hasWebHID = MochimoTransport.isWebHIDSupported();
const hasWebBLE = MochimoTransport.isWebBLESupported();import { MochimoApp } from 'ledger-mochimo';
class MochimoWallet {
private app: MochimoApp;
constructor() {
this.app = new MochimoApp();
}
async connect() {
await this.app.connect();
}
async getAddresses(account: number, count: number) {
const addresses = [];
for (let i = 0; i < count; i++) {
const addr = await this.app.getAddress({
account,
addressIndex: i
});
addresses.push(addr);
}
return addresses;
}
async sendTransaction(to: string, amount: bigint, fee: bigint) {
// Implementation depends on your address format and UTXO management
const transaction = {
bip32Path: { account: 0, addressIndex: 0 },
destinations: [{
tag: hexToBytes(to), // Convert address to bytes
ref: 'Payment',
amount
}],
sourceTotal: amount + fee,
feeTotal: fee,
nonce: await this.getCurrentBlockNumber(),
blkToLive: 0n
};
return await this.app.signTransactionComplete(transaction);
}
}// capacitor.config.ts
import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
// Enable Bluetooth permissions
plugins: {
BluetoothLe: {
displayStrings: {
scanning: "Scanning for Ledger device...",
cancel: "Cancel",
connect: "Connect",
connectedTo: "Connected to Ledger"
}
}
}
};
// In your app
import { MochimoApp } from 'ledger-mochimo';
const app = new MochimoApp();
// Only Bluetooth is available on mobile
await app.connect('webble');# Install dependencies
npm install
# Build the library
npm run build
# Run tests
npm test
# Lint code
npm run lint
# Development mode (watch)
npm run devMIT
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Documentation: See the Mochimo Ledger App Protocol
- Issues: Report bugs on GitHub Issues
- Discussions: Join the community discussions
- Mochimo Core - Official Mochimo blockchain implementation
- Ledger Live - Official Ledger wallet application
- @ledgerhq/hw-transport - Ledger transport libraries