AgentSkillsCN

wallet-brc100

当用户请求“实现 BRC-100 钱包”、“使用 wallet-toolbox”、“TypeScript BSV 钱包”、“BRC-100 实现”、“桌面钱包”、“Electron 钱包”、“浏览器钱包”、“IndexedDB 钱包存储”、“钱包操作”、“钱包篮子”、“UTXO 管理”、“createAction”、“listOutputs”、“钱包证书”,或需要借助 @bsv/wallet-toolbox 指导构建符合标准的钱包时,应使用此技能。

SKILL.md
--- frontmatter
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 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

ClassPurposeUse When
WalletFull BRC-100 walletBuilding production wallet apps
SimpleWalletManagerLightweight wrapperSimple key-based authentication
CWIStyleWalletManagerMulti-profile walletAdvanced UMP token flows
WalletSignerTransaction signingCustom signing logic

Table of Contents

  1. Installation & Setup
  2. Wallet Initialization
  3. Transaction Operations
  4. Key Management
  5. Storage Configuration
  6. Certificate Operations
  7. Error Handling
  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<number, { unlockingScript: string }>
) {
  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<WalletDB>('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<string> {
  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:

SkillRelationship
encrypt-decrypt-backupStandard backup formats (.bep files, AES-256-GCM)
junglebusPopulate UTXO set from blockchain, real-time streaming
key-derivationType42/BRC-42 and BIP32 key derivation details
wallet-encrypt-decryptECDH message encryption patterns
wallet-send-bsvBasic 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 for:

  • wallet-create-ordinals - Mint inscriptions
  • extract-blockchain-media - Extract media from transactions
  • ordinals-marketplace - Browse GorillaPool marketplace

Additional Resources

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:

PlatformGuideReference Implementation
Browser Extensionextension-guide.mdyours-wallet
Desktop (Electron)desktop-guide.mdbsv-desktop
Web Applicationweb-guide.md-
Mobile (React Native)mobile-guide.md-
Node.js Service/CLInodejs-guide.md-

See 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

TaskMethodKey Args
Send BSVcreateAction()outputs, options
Check balancebalance()None
List UTXOslistOutputs()basket, spendable
Get historylistActions()labels, limit
Get pubkeygetPublicKey()protocolID, keyID
Encrypt dataencrypt()plaintext, counterparty
Get certificateacquireCertificate()type, certifier

Remember: Always handle errors properly, use privileged keys securely, and follow BRC-100 security levels for sensitive operations!