--- name: wallet-brc100 description: This skill should be used when the user asks to "implement BRC-100 wallet", "use wallet-toolbox", "TypeScript BSV wallet", "BRC-100 implementation", "desktop wallet", "Electron wallet", "browser wallet", "IndexedDB wallet storage", "wallet actions", "wallet baskets", "UTXO management", "createAction", "listOutputs", "wallet certificates", or needs guidance on building conforming wallets using @bsv/wallet-toolbox. --- # BRC-100 Wallet Implementation Guide This skill provides comprehensive guidance for implementing BRC-100 conforming wallets using the `@bsv/wallet-toolbox` package (v1.7.18+). **Getting Started**: Before implementing, review the [Strategic Questionnaire](./references/strategic-questionnaire.md) to determine the right architecture for your wallet type. ## Quick Reference ### Core Dependencies ```json { "@bsv/wallet-toolbox": "^1.7.18", "@bsv/sdk": "^1.9.29" } ``` ### Main Classes | Class | Purpose | Use When | |-------|---------|----------| | **Wallet** | Full BRC-100 wallet | Building production wallet apps | | **SimpleWalletManager** | Lightweight wrapper | Simple key-based authentication | | **CWIStyleWalletManager** | Multi-profile wallet | Advanced UMP token flows | | **WalletSigner** | Transaction signing | Custom signing logic | --- ## Table of Contents 1. [Installation & Setup](#1-installation--setup) 2. [Wallet Initialization](#2-wallet-initialization) 3. [Transaction Operations](#3-transaction-operations) 4. [Key Management](#4-key-management) 5. [Storage Configuration](#5-storage-configuration) 6. [Certificate Operations](#6-certificate-operations) 7. [Error Handling](#7-error-handling) 8. [Production Patterns](#8-production-patterns) --- ## 1. Installation & Setup ### Install Dependencies ```bash npm install @bsv/wallet-toolbox @bsv/sdk # Optional storage backends: npm install knex sqlite3 # SQLite npm install knex mysql2 # MySQL npm install idb # IndexedDB (browser) ``` ### Basic Imports ```typescript import { Wallet, WalletStorageManager, StorageKnex, StorageIdb, Services, WalletServices, PrivilegedKeyManager } from '@bsv/wallet-toolbox' import { PrivateKey, KeyDeriver, Random, Utils } from '@bsv/sdk' ``` --- ## 2. Wallet Initialization ### Pattern A: Simple Wallet (Node.js with SQLite) ```typescript import { Wallet, StorageKnex, Services } from '@bsv/wallet-toolbox' import { PrivateKey, Random } from '@bsv/sdk' import Knex from 'knex' async function createSimpleWallet() { // 1. Create root private key (or derive from mnemonic) const rootKey = new PrivateKey(Random(32)) // Use KeyDeriver from @bsv/sdk for proper BRC-42 key derivation const keyDeriver = new KeyDeriver(rootKey) // 2. Configure SQLite storage const knex = Knex({ client: 'sqlite3', connection: { filename: './wallet.db' }, useNullAsDefault: true }) const storage = new StorageKnex({ knex, storageIdentityKey: rootKey.toPublicKey().toString(), storageName: 'my-wallet-storage' }) await storage.makeAvailable() // 3. Configure services (mainnet) const services = new Services({ chain: 'main', bsvExchangeRate: { timestamp: new Date(), base: 'USD', rate: 50 }, bsvUpdateMsecs: 15 * 60 * 1000, fiatExchangeRates: { timestamp: new Date(), base: 'USD', rates: { EUR: 0.85, GBP: 0.73 } }, fiatUpdateMsecs: 24 * 60 * 60 * 1000, arcUrl: 'https://arc.taal.com', arcConfig: {} }) // 4. Create wallet const wallet = new Wallet({ chain: 'main', keyDeriver, storage, services }) return wallet } ``` ### Pattern B: Browser Wallet (IndexedDB) ```typescript import { Wallet, StorageIdb, Services } from '@bsv/wallet-toolbox' import { PrivateKey, Random } from '@bsv/sdk' async function createBrowserWallet() { const rootKey = new PrivateKey(Random(32)) // Use IndexedDB for browser storage const storage = new StorageIdb({ idb: await openDB('my-wallet-db', 1), storageIdentityKey: rootKey.toPublicKey().toString(), storageName: 'browser-wallet' }) await storage.makeAvailable() const services = new Services({ chain: 'main', // ... services config }) const wallet = new Wallet({ chain: 'main', keyDeriver: createKeyDeriver(rootKey), storage, services }) return wallet } ``` ### Pattern C: Multi-Profile Wallet ```typescript import { CWIStyleWalletManager, OverlayUMPTokenInteractor } from '@bsv/wallet-toolbox' async function createMultiProfileWallet() { const manager = new CWIStyleWalletManager( 'example.com', // Admin originator async (profilePrimaryKey, profilePrivilegedKeyManager, profileId) => { // Build wallet for specific profile const keyDeriver = createKeyDeriver(new PrivateKey(profilePrimaryKey)) const storage = await createStorage(profileId) const services = new Services({ chain: 'main', /* ... */ }) return new Wallet({ chain: 'main', keyDeriver, storage, services, privilegedKeyManager: profilePrivilegedKeyManager }) }, new OverlayUMPTokenInteractor(), // UMP token interactor async (recoveryKey) => { // Save recovery key (e.g., prompt user to write it down) console.log('SAVE THIS RECOVERY KEY:', Utils.toBase64(recoveryKey)) return true }, async (reason, test) => { // Retrieve password from user const password = prompt(`Enter password for: ${reason}`) if (!password) throw new Error('Password required') if (!test(password)) throw new Error('Invalid password') return password } ) // Provide presentation key (e.g., from QR code scan) const presentationKey = Random(32) await manager.providePresentationKey(presentationKey) // Provide password await manager.providePassword('user-password') // Now authenticated and ready to use return manager } ``` --- ## 3. Transaction Operations ### Create a Transaction ```typescript import { CreateActionArgs, CreateActionResult } from '@bsv/sdk' async function sendBSV( wallet: Wallet, recipientAddress: string, satoshis: number ) { const args: CreateActionArgs = { description: 'Send BSV payment', outputs: [{ lockingScript: Script.fromAddress(recipientAddress).toHex(), satoshis, outputDescription: `Payment to ${recipientAddress}`, basket: 'default', tags: ['payment'] }], options: { acceptDelayedBroadcast: false, // Broadcast immediately randomizeOutputs: true // Privacy } } const result: CreateActionResult = await wallet.createAction(args) if (result.txid) { console.log('Transaction created:', result.txid) return result.txid } else { console.log('Transaction pending signature') return result.signableTransaction } } ``` ### Sign a Transaction ```typescript async function signTransaction( wallet: Wallet, reference: string, unlockingScripts: Record ) { const result = await wallet.signAction({ reference, spends: unlockingScripts }) console.log('Transaction signed:', result.txid) return result } ``` ### Check Wallet Balance ```typescript async function getWalletBalance(wallet: Wallet) { // Method 1: Quick balance (uses special operation) const balance = await wallet.balance() console.log(`Balance: ${balance} satoshis`) // Method 2: Detailed balance with UTXOs const detailed = await wallet.balanceAndUtxos('default') console.log(`Total: ${detailed.total} satoshis`) console.log(`UTXOs: ${detailed.utxos.length}`) detailed.utxos.forEach(utxo => { console.log(` ${utxo.outpoint}: ${utxo.satoshis} sats`) }) return balance } ``` ### List Outputs ```typescript import { ListOutputsArgs, ListOutputsResult } from '@bsv/sdk' async function listSpendableOutputs(wallet: Wallet) { const args: ListOutputsArgs = { basket: 'default', // Change basket spendable: true, // Only spendable outputs limit: 100, offset: 0, tags: ['payment'] // Optional: filter by tags } const result: ListOutputsResult = await wallet.listOutputs(args) console.log(`Found ${result.totalOutputs} outputs`) result.outputs.forEach(output => { console.log(` ${output.outpoint}: ${output.satoshis} sats`) }) return result } ``` ### List Actions (Transactions) ```typescript async function listTransactionHistory(wallet: Wallet) { const result = await wallet.listActions({ labels: [], labelQueryMode: 'any', limit: 50, offset: 0 }) console.log(`Found ${result.totalActions} actions`) result.actions.forEach(action => { console.log(` ${action.txid}: ${action.status} - ${action.description}`) }) return result } ``` --- ## 4. Key Management ### Get Public Key ```typescript async function getIdentityKey(wallet: Wallet) { // Get wallet's identity key const result = await wallet.getPublicKey({ identityKey: true }) console.log('Identity Key:', result.publicKey) return result.publicKey } async function getDerivedKey(wallet: Wallet) { // Get derived key for specific protocol const result = await wallet.getPublicKey({ protocolID: [2, 'my-app'], keyID: 'encryption-key-1', counterparty: 'recipient-identity-key' }) return result.publicKey } ``` ### Encrypt/Decrypt Data ```typescript async function encryptMessage( wallet: Wallet, plaintext: string, recipientPubKey: string ) { const result = await wallet.encrypt({ plaintext: Utils.toArray(plaintext, 'utf8'), protocolID: [2, 'secure-messaging'], keyID: 'msg-key', counterparty: recipientPubKey }) return Utils.toBase64(result.ciphertext) } async function decryptMessage( wallet: Wallet, ciphertext: string, senderPubKey: string ) { const result = await wallet.decrypt({ ciphertext: Utils.toArray(ciphertext, 'base64'), protocolID: [2, 'secure-messaging'], keyID: 'msg-key', counterparty: senderPubKey }) return Utils.toUTF8(result.plaintext) } ``` ### Create Signature ```typescript async function signData(wallet: Wallet, data: string) { const result = await wallet.createSignature({ data: Utils.toArray(data, 'utf8'), protocolID: [2, 'document-signing'], keyID: 'sig-key', counterparty: 'self' }) return Utils.toBase64(result.signature) } ``` --- ## 5. Storage Configuration ### SQLite Storage (Node.js) ```typescript import Knex from 'knex' import { StorageKnex } from '@bsv/wallet-toolbox' async function setupSQLiteStorage() { const knex = Knex({ client: 'sqlite3', connection: { filename: './wallet.db' }, useNullAsDefault: true }) const storage = new StorageKnex({ knex, storageIdentityKey: 'your-identity-key', storageName: 'main-storage' }) await storage.makeAvailable() return storage } ``` ### MySQL Storage ```typescript async function setupMySQLStorage() { const knex = Knex({ client: 'mysql2', connection: { host: 'localhost', user: 'wallet_user', password: 'secure_password', database: 'wallet_db' } }) const storage = new StorageKnex({ knex, storageIdentityKey: 'your-identity-key', storageName: 'mysql-storage' }) await storage.makeAvailable() return storage } ``` ### IndexedDB Storage (Browser) ```typescript import { openDB, DBSchema } from 'idb' import { StorageIdb } from '@bsv/wallet-toolbox' interface WalletDB extends DBSchema { // Storage schema is managed by StorageIdb } async function setupIndexedDBStorage() { const db = await openDB('wallet-db', 1, { upgrade(db) { // StorageIdb creates necessary object stores } }) const storage = new StorageIdb({ idb: db, storageIdentityKey: 'your-identity-key', storageName: 'browser-storage' }) await storage.makeAvailable() return storage } ``` ### Multi-Storage Manager ```typescript import { WalletStorageManager } from '@bsv/wallet-toolbox' async function setupMultiStorage() { const primaryStorage = await setupSQLiteStorage() const backupStorage = await setupMySQLStorage() const manager = new WalletStorageManager( primaryStorage, [backupStorage] ) return manager } ``` --- ## 6. Certificate Operations ### Acquire Certificate (Direct Protocol) ```typescript import { AcquireCertificateArgs } from '@bsv/sdk' async function acquireDirectCertificate(wallet: Wallet) { const args: AcquireCertificateArgs = { acquisitionProtocol: 'direct', type: 'https://example.com/user-certificate', certifier: 'certifier-identity-key', serialNumber: Utils.toArray('cert-serial-123', 'utf8'), subject: await wallet.getIdentityKey(), revocationOutpoint: 'txid.vout', fields: { name: Utils.toArray('Alice Smith', 'utf8'), email: Utils.toArray('alice@example.com', 'utf8') }, keyringForSubject: { /* master keyring */ }, signature: Utils.toArray('signature-bytes', 'base64') } const result = await wallet.acquireCertificate(args) console.log('Certificate acquired:', result.certificateId) return result } ``` ### Acquire Certificate (Issuance Protocol) ```typescript async function requestCertificateIssuance(wallet: Wallet) { const args: AcquireCertificateArgs = { acquisitionProtocol: 'issuance', type: 'https://example.com/kyc-certificate', certifier: 'certifier-identity-key', certifierUrl: 'https://certifier.example.com', fields: { name: 'Alice Smith', birthdate: '1990-01-01', country: 'US' } } const result = await wallet.acquireCertificate(args) console.log('Certificate issued:', result.certificateId) return result } ``` ### List Certificates ```typescript async function listMyCertificates(wallet: Wallet) { const result = await wallet.listCertificates({ certifiers: ['certifier-identity-key'], types: ['https://example.com/user-certificate'], limit: 50, offset: 0 }) console.log(`Found ${result.totalCertificates} certificates`) result.certificates.forEach(cert => { console.log(` Type: ${cert.type}, Certifier: ${cert.certifier}`) }) return result } ``` ### Prove Certificate ```typescript async function proveCertificate(wallet: Wallet, certificateId: string) { const result = await wallet.proveCertificate({ certificateId, fieldsToReveal: ['name', 'email'], verifier: 'verifier-identity-key', privileged: false }) console.log('Certificate proof:', result.keyringForVerifier) return result } ``` --- ## 7. Error Handling ### Standard Error Types ```typescript import { WalletError, WERR_INVALID_PARAMETER, WERR_INTERNAL, WERR_REVIEW_ACTIONS } from '@bsv/wallet-toolbox' try { const result = await wallet.createAction({ description: 'Test transaction', outputs: [/* ... */] }) } catch (eu: unknown) { const error = WalletError.fromUnknown(eu) if (error.name === 'WERR_INVALID_PARAMETER') { console.error('Invalid parameter:', error.message) console.error('Stack:', error.stack) } else if (error.name === 'WERR_REVIEW_ACTIONS') { // Handle transaction review errors console.error('Review required:', error.details) } else { console.error('Wallet error:', error.message) } } ``` ### Review Actions Error (Transaction Failed) ```typescript async function handleCreateAction(wallet: Wallet) { try { return await wallet.createAction({ description: 'Payment', outputs: [/* ... */] }) } catch (eu: unknown) { const error = WalletError.fromUnknown(eu) if (error.name === 'WERR_REVIEW_ACTIONS') { // Access detailed failure info const details = error as any console.error('Delayed results:', details.notDelayedResults) console.error('Send results:', details.sendWithResults) console.error('Failed txid:', details.txid) // Handle double-spend if (details.notDelayedResults?.some(r => r.status === 'doubleSpend')) { console.error('Double-spend detected!') console.error('Competing txs:', details.notDelayedResults[0].competingTxs) } } throw error } } ``` --- ## 8. Production Patterns ### Pattern: Wallet State Management ```typescript class WalletManager { private wallet: Wallet | null = null async initialize(rootKey: PrivateKey) { if (this.wallet) { throw new Error('Wallet already initialized') } const storage = await this.setupStorage() const services = this.setupServices() const keyDeriver = this.createKeyDeriver(rootKey) this.wallet = new Wallet({ chain: 'main', keyDeriver, storage, services }) return this.wallet } async destroy() { if (this.wallet) { await this.wallet.destroy() this.wallet = null } } getWallet(): Wallet { if (!this.wallet) { throw new Error('Wallet not initialized') } return this.wallet } private async setupStorage() { // Storage setup logic return await setupSQLiteStorage() } private setupServices() { return new Services({ chain: 'main', // ... config }) } private createKeyDeriver(rootKey: PrivateKey) { // Key derivation logic return { rootKey, identityKey: rootKey.toPublicKey().toString(), // ... derivation methods } } } ``` ### Pattern: Transaction Retry Logic ```typescript async function sendWithRetry( wallet: Wallet, args: CreateActionArgs, maxRetries = 3 ): Promise { let lastError: Error | null = null for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const result = await wallet.createAction(args) if (result.txid) { return result.txid } // Handle signature if needed if (result.signableTransaction) { const signed = await wallet.signAction({ reference: result.signableTransaction.reference, spends: {} // Provide unlocking scripts }) return signed.txid! } throw new Error('Unexpected result format') } catch (error) { lastError = error as Error console.error(`Attempt ${attempt} failed:`, error) if (attempt < maxRetries) { await new Promise(resolve => setTimeout(resolve, 1000 * attempt)) } } } throw lastError || new Error('All retries failed') } ``` ### Pattern: Background Transaction Monitor ```typescript import { Monitor } from '@bsv/wallet-toolbox' async function setupMonitor(wallet: Wallet) { const monitor = new Monitor({ storage: wallet.storage, services: wallet.services, chain: 'main' }) monitor.on('transaction', (status) => { console.log('Transaction update:', status.txid, status.blockHeight) }) monitor.on('error', (error) => { console.error('Monitor error:', error) }) await monitor.start() return monitor } ``` --- ## Related Skills For comprehensive wallet development, also reference these skills: | Skill | Relationship | |-------|--------------| | `encrypt-decrypt-backup` | Standard backup formats (.bep files, AES-256-GCM) | | `junglebus` | Populate UTXO set from blockchain, real-time streaming | | `key-derivation` | Type42/BRC-42 and BIP32 key derivation details | | `wallet-encrypt-decrypt` | ECDH message encryption patterns | | `wallet-send-bsv` | Basic transaction creation (simpler than BRC-100) | **For 1Sat Ordinals / Token Support:** If your BRC-100 wallet needs to handle 1Sat Ordinals, BSV-20/BSV-21 tokens, or inscriptions, use `@1sat/wallet-toolbox` which wraps the core wallet-toolbox with ordinals capabilities. See [1sat-skills](https://github.com/b-open-io/1sat-skills) for: - `wallet-create-ordinals` - Mint inscriptions - `extract-blockchain-media` - Extract media from transactions - `ordinals-marketplace` - Browse GorillaPool marketplace --- ## Additional Resources - **BRC-100 Specification**: https://bsv.brc.dev/wallet/0100 - **BRC-42 (BKDS)**: https://bsv.brc.dev/wallet/0042 - **BRC-43 (Security Levels)**: https://bsv.brc.dev/wallet/0043 - **Wallet Toolbox Docs**: https://bsv-blockchain.github.io/wallet-toolbox - **BSV SDK Docs**: https://bsv-blockchain.github.io/ts-sdk - **@1sat/wallet-toolbox**: BRC-100 wallet with 1Sat Ordinals support (wraps @bsv/wallet-toolbox) - **1sat-skills**: https://github.com/b-open-io/1sat-skills - Ordinals minting and management - **bsv-desktop (Reference Implementation)**: https://github.com/bsv-blockchain/bsv-desktop - Real-world Electron wallet with BRC-100 support - IPC architecture for storage isolation - Background monitoring patterns - HTTPS server on port 2121 for BRC-100 interface **Research**: For deep dives into BRC specifications or implementation patterns, use the browser-agent to fetch current documentation from bsv.brc.dev. --- ## Platform Guides Platform-specific implementation guides: | Platform | Guide | Reference Implementation | |----------|-------|-------------------------| | Browser Extension | [extension-guide.md](./references/extension-guide.md) | [yours-wallet](https://github.com/AustinKelsay/yours-wallet) | | Desktop (Electron) | [desktop-guide.md](./references/desktop-guide.md) | [bsv-desktop](https://github.com/bsv-blockchain/bsv-desktop) | | Web Application | [web-guide.md](./references/web-guide.md) | - | | Mobile (React Native) | [mobile-guide.md](./references/mobile-guide.md) | - | | Node.js Service/CLI | [nodejs-guide.md](./references/nodejs-guide.md) | - | See [references/key-concepts.md](./references/key-concepts.md) for BRC-100 unique concepts: - Actions vs Transactions - Baskets and Tags - Certificate system (BRC-52/53/64/65) - Background Monitoring --- ## Common Patterns Summary | Task | Method | Key Args | |------|--------|----------| | Send BSV | `createAction()` | `outputs`, `options` | | Check balance | `balance()` | None | | List UTXOs | `listOutputs()` | `basket`, `spendable` | | Get history | `listActions()` | `labels`, `limit` | | Get pubkey | `getPublicKey()` | `protocolID`, `keyID` | | Encrypt data | `encrypt()` | `plaintext`, `counterparty` | | Get certificate | `acquireCertificate()` | `type`, `certifier` | --- **Remember**: Always handle errors properly, use privileged keys securely, and follow BRC-100 security levels for sensitive operations!