OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-wallet / hyper-wallet-advanced-cli.cjs

2306 lines · javascript ref:
#!/usr/bin/env bare

// hyper-wallet-autobase.cjs - Multi-Device CLI with Autobase (Phase 5.2)
// CRITICAL: Load bare-node-runtime FIRST before any other requires
require('bare-node-runtime/global')

const fs = require('bare-fs')
const path = require('bare-path')
const os = require('bare-os')
const IdentityKey = require('keet-identity-key')
const b4a = require('b4a')
const fetch = require('bare-fetch')
const {
  initializeStorage,
  bootstrapDevice,
  createInvite,
  joinWallet,
  getCurrentDevice,
  listDevices,
  removeDevice,
  getConnectionStatus,
  waitForNewDevice,
  getPendingWriters,
  approveWriter,
  enableReplication,
  closeStorage,
  // UTXO functions (Phase 6.3)
  storeUTXO,
  getUTXOs,
  updateUTXO,
  deleteUTXO,
  // Transaction storage (Phase 6.6)
  storeTransaction,
  getTransactions,
  getTransaction,
  deleteTransaction,
  // Transaction status updates (Phase 6.7)
  updateTransactionStatus
} = require('./lib/hyper-wallet-lib/wallet-storage.cjs')
const {
  generateWalletMnemonic,
  saveWalletMnemonic,
  loadWalletMnemonic,
  initializeWDK,
  getFirstAddress,
  walletMnemonicExists
} = require('./lib/hyper-wallet-lib/wdk-integration.cjs')
const {
  generateNewAddress,
  listAddresses: listAddressesFromManager,
  getCurrentAddress: getCurrentAddressFromManager
} = require('./lib/hyper-wallet-lib/address-manager.cjs')
const { loadConfig } = require('./lib/hyper-wallet-lib/config-loader.cjs')
// UTXO manager functions (not needed for WDK-based balance queries)
// const {
//   storeUTXO: storeUTXOHelper,
//   markUTXOSpent,
//   calculateBalance,
//   getSpendableUTXOs,
//   formatBalance
// } = require('./lib/hyper-wallet-lib/utxo-manager.cjs')
// const {
//   getUTXOsForAddresses,
//   getFeeRates,
//   checkConnection
// } = require('./lib/hyper-wallet-lib/blockchain-api.cjs')

// Load configuration from file
const config = loadConfig()

// Storage configuration
const STORAGE_DIR = path.join(os.homedir(), '.hyper-wallet')
const MNEMONIC_FILE = path.join(STORAGE_DIR, '.mnemonic')

// ============================================================================
// STORAGE FUNCTIONS
// ============================================================================

function ensureStorageDir() {
  if (!fs.existsSync(STORAGE_DIR)) {
    fs.mkdirSync(STORAGE_DIR, { recursive: true, mode: 0o700 })
  }
}

function saveMnemonic(mnemonic) {
  ensureStorageDir()
  fs.writeFileSync(MNEMONIC_FILE, mnemonic, { mode: 0o600 })
}

function loadMnemonic() {
  if (!fs.existsSync(MNEMONIC_FILE)) {
    return null
  }
  return fs.readFileSync(MNEMONIC_FILE, 'utf8').trim()
}

// ============================================================================
// IDENTITY MANAGEMENT
// ============================================================================

async function loadIdentity() {
  const mnemonic = loadMnemonic()
  if (!mnemonic) {
    throw new Error('Identity not initialized. Run: bare hyper-wallet-autobase.cjs init')
  }

  const identity = await IdentityKey.from({ mnemonic })
  return identity
}

// ============================================================================
// COMMANDS
// ============================================================================

async function cmdInit(args) {
  console.log('🔐 Initializing Hyper Wallet with Multi-Device Support')
  console.log('')

  const existing = loadMnemonic()
  if (existing) {
    console.log('⚠️  Wallet already initialized!')
    console.log('')
    console.log('Your identity mnemonic is stored at:')
    console.log(MNEMONIC_FILE)
    console.log('')
    console.log('To see your identity:')
    console.log('  bare hyper-wallet-autobase.cjs whoami')
    console.log('')
    console.log('To add another device:')
    console.log('  bare hyper-wallet-autobase.cjs device add')
    console.log('')
    return
  }

  // Generate new identity
  const mnemonic = IdentityKey.generateMnemonic()
  const identity = await IdentityKey.from({ mnemonic })

  saveMnemonic(mnemonic)

  console.log('✅ Identity created!')
  console.log('')
  console.log('Your 24-word IDENTITY mnemonic (KEEP SAFE):')
  console.log('')
  console.log('  ' + mnemonic)
  console.log('')
  console.log('⚠️  IMPORTANT: Write this down and store it safely!')
  console.log('   This is for DEVICE ATTESTATION only.')
  console.log('')
  console.log('Public Key:', b4a.toString(identity.identityKeyPair.publicKey, 'hex').slice(0, 16) + '...')
  console.log('')

  // Generate wallet mnemonic (separate from identity)
  console.log('Generating wallet mnemonic (for cryptocurrency keys)...')
  const walletMnemonic = generateWalletMnemonic()
  saveWalletMnemonic(walletMnemonic)

  console.log('✅ Wallet mnemonic created!')
  console.log('')
  console.log('Your 12-word WALLET mnemonic (KEEP SAFE):')
  console.log('')
  console.log('  ' + walletMnemonic)
  console.log('')
  console.log('⚠️  CRITICAL: This is SEPARATE from your identity mnemonic!')
  console.log('   Identity (24 words) = Device access')
  console.log('   Wallet (12 words)   = Cryptocurrency funds')
  console.log('')
  console.log('⚠️  You must backup BOTH mnemonics separately!')
  console.log('')

  // Initialize storage
  await initializeStorage(identity)

  console.log('Next steps:')
  console.log('  1. Bootstrap this device:')
  console.log('     bare hyper-wallet-autobase.cjs device bootstrap "My Laptop"')
  console.log('')
  console.log('  2. Initialize WDK (test address generation):')
  console.log('     bare hyper-wallet-autobase.cjs wallet init')
  console.log('')
}

async function cmdWhoami() {
  const identity = await loadIdentity()

  console.log('👤 Identity Information')
  console.log('')
  console.log('Public Key:', b4a.toString(identity.identityKeyPair.publicKey, 'hex'))
  console.log('')

  // Initialize storage to get device info
  await initializeStorage(identity)

  const currentDevice = getCurrentDevice()
  if (currentDevice) {
    console.log('Current Device:')
    console.log(`  Name:    ${currentDevice.name}`)
    console.log(`  Added:   ${new Date(currentDevice.addedAt).toLocaleString()}`)
    console.log(`  Writer:  ${b4a.toString(currentDevice.key, 'hex').slice(0, 16)}...`)
    console.log('')
  } else {
    console.log('⚠️  Device not bootstrapped yet')
    console.log('')
    console.log('Run: bare hyper-wallet-autobase.cjs device bootstrap "Device Name"')
    console.log('')
  }

  await closeStorage()
}

// ============================================================================
// WALLET COMMANDS (WDK Integration)
// ============================================================================

async function cmdWalletInit(args) {
  console.log('💰 Initializing Wallet (WDK)')
  console.log('')

  // Check if wallet mnemonic exists
  if (!walletMnemonicExists()) {
    console.log('❌ No wallet mnemonic found!')
    console.log('')
    console.log('Run: bare hyper-wallet-autobase.cjs init')
    console.log('')
    process.exit(1)
  }

  // Load wallet mnemonic
  const walletMnemonic = loadWalletMnemonic()

  console.log('Initializing WDK...')

  try {
    // Initialize WDK (testnet mode)
    const { wdk } = await initializeWDK(walletMnemonic, { testnet: true })

    console.log('✅ WDK initialized')
    console.log('')

    // Get first addresses for testing
    console.log('Deriving first addresses...')
    const btcAddress = await getFirstAddress(wdk, 'bitcoin')
    const ethAddress = await getFirstAddress(wdk, 'ethereum')

    console.log('')
    console.log('Bitcoin (Testnet):')
    console.log(`  Address: ${btcAddress}`)
    console.log(`  Path:    m/84'/1'/0'/0/0`)
    console.log(`  Network: testnet`)
    console.log('')
    console.log('Ethereum (Sepolia):')
    console.log(`  Address: ${ethAddress}`)
    console.log(`  Path:    m/44'/60'/0'/0/0`)
    console.log(`  Network: sepolia`)
    console.log('')

    console.log('✅ WDK initialization successful!')
    console.log('')
    console.log('Next steps:')
    console.log('  1. Generate addresses: bare hyper-wallet-autobase.cjs address new btc')
    console.log('  2. Get testnet coins:  https://testnet-faucet.mempool.co/')
    console.log('')
  } catch (err) {
    console.log('')
    console.log('❌ WDK initialization failed:', err.message)
    console.log('')
    if (process.env.DEBUG) {
      console.log('Stack trace:')
      console.log(err.stack)
      console.log('')
    }
    process.exit(1)
  }
}

// ============================================================================
// ADDRESS COMMANDS (Phase 6.2)
// ============================================================================

async function cmdAddressNew(args) {
  const chainArg = args[2] || 'testnet-bitcoin'

  // Parse chain argument
  let chain, network
  if (chainArg.startsWith('testnet-')) {
    network = 'testnet'
    chain = chainArg.replace('testnet-', '')
  } else if (chainArg.startsWith('mainnet-')) {
    network = 'mainnet'
    chain = chainArg.replace('mainnet-', '')
  } else {
    // Legacy support
    network = 'testnet'
    chain = chainArg
  }

  console.log('🔑 Generating New Address')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  try {
    console.log(`Chain: ${chainArg}`)
    console.log('Deriving address...')
    console.log('')

    const addr = await generateNewAddress(chain, { testnet: network === 'testnet' })

    console.log('✅ Address generated!')
    console.log('')
    console.log(`Chain:   ${addr.chain}`)
    console.log(`Address: ${addr.address}`)
    console.log(`Path:    ${addr.path}`)
    console.log(`Index:   ${addr.index}`)
    console.log('')
    console.log('📡 Synced to all devices.')
    console.log('')
  } catch (err) {
    console.log('')
    console.log('❌ Error generating address:', err.message)
    console.log('')
    if (process.env.DEBUG) {
      console.log('Stack trace:')
      console.log(err.stack)
      console.log('')
    }
  }

  await closeStorage()
}

async function cmdAddressList(args) {
  const chain = args[2] || 'all'

  console.log('📋 Address List')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  try {
    const addresses = await listAddressesFromManager(chain)

    if (addresses.length === 0) {
      console.log(`No addresses found for ${chain}.`)
      console.log('')
      console.log('Generate an address:')
      console.log('  bare hyper-wallet-autobase.cjs address new bitcoin')
      console.log('')
      await closeStorage()
      return
    }

    for (const addr of addresses) {
      const icon = addr.chain === 'bitcoin' ? '₿' : '⟠'
      console.log(`${icon} ${addr.chain} #${addr.index}`)
      console.log(`   ${addr.address}`)
      console.log(`   ${addr.path}`)
      console.log(`   Device: ${addr.device}`)
      console.log(`   Created: ${new Date(addr.createdAt).toLocaleString()}`)
      console.log('')
    }

    console.log(`Total: ${addresses.length} address${addresses.length === 1 ? '' : 'es'}`)
    console.log('')
  } catch (err) {
    console.log('')
    console.log('❌ Error listing addresses:', err.message)
    console.log('')
  }

  await closeStorage()
}

async function cmdAddressCurrent(args) {
  const chainArg = args[2]

  if (!chainArg) {
    console.error('❌ Missing chain argument')
    console.error('')
    console.error('Usage: bare hyper-wallet-autobase.cjs address current <chain>')
    console.error('Example: bare hyper-wallet-autobase.cjs address current testnet-bitcoin')
    console.error('         bare hyper-wallet-autobase.cjs address current testnet-ethereum')
    console.error('')
    process.exit(1)
  }

  // Parse chain argument
  let chain
  if (chainArg.startsWith('testnet-')) {
    chain = chainArg.replace('testnet-', '')
  } else if (chainArg.startsWith('mainnet-')) {
    chain = chainArg.replace('mainnet-', '')
  } else {
    // Legacy support
    chain = chainArg
  }

  console.log('🔑 Current Address')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  try {
    const addr = await getCurrentAddressFromManager(chain)

    if (!addr) {
      console.log(`No addresses found for ${chainArg}.`)
      console.log('')
      console.log('Generate an address:')
      console.log(`  bare hyper-wallet-autobase.cjs address new ${chainArg}`)
      console.log('')
      await closeStorage()
      return
    }

    const icon = addr.chain === 'bitcoin' ? '₿' : '⟠'
    console.log(`${icon} ${chainArg} #${addr.index}`)
    console.log(`   ${addr.address}`)
    console.log(`   ${addr.path}`)
    console.log('')
  } catch (err) {
    console.log('')
    console.log('❌ Error getting current address:', err.message)
    console.log('')
  }

  await closeStorage()
}

// ============================================================================
// UTXO & BALANCE COMMANDS (Phase 6.3)
// ============================================================================

async function cmdBalance(args) {
  const chainArg = args[1] || 'testnet-bitcoin'

  console.log('💰 Balance')
  console.log('')

  // Parse chain argument (testnet-bitcoin, testnet4-bitcoin, testnet-ethereum, bitcoin, ethereum)
  let chain, network, testnet4 = false
  if (chainArg.startsWith('testnet4-')) {
    network = 'testnet'
    testnet4 = true
    chain = chainArg.replace('testnet4-', '')
  } else if (chainArg.startsWith('testnet-')) {
    network = 'testnet'
    chain = chainArg.replace('testnet-', '')
  } else if (chainArg.startsWith('mainnet-')) {
    network = 'mainnet'
    chain = chainArg.replace('mainnet-', '')
  } else {
    // Legacy support: assume testnet for backward compatibility
    network = 'testnet'
    chain = chainArg
    console.log(`⚠️  Using testnet (use 'testnet-${chain}' to be explicit)`)
    console.log('')
  }

  const identity = await loadIdentity()
  await initializeStorage(identity)

  try {
    // Check if wallet mnemonic exists
    const walletMnemonic = loadWalletMnemonic()
    if (!walletMnemonic) {
      console.log('❌ No wallet mnemonic found!')
      console.log('')
      console.log('Initialize WDK first:')
      console.log('  bare hyper-wallet-autobase.cjs wallet init')
      console.log('')
      await closeStorage()
      return
    }

    // Initialize WDK and get account
    const { wdk } = await initializeWDK(walletMnemonic, { testnet: network === 'testnet', testnet4 })

    let account
    if (chain === 'bitcoin') {
      const networkLabel = testnet4 ? 'testnet4' : network
      console.log(`📡 Connecting to ${networkLabel} blockchain...`)
      account = await wdk.getAccount('bitcoin', 0)
    } else if (chain === 'ethereum') {
      console.log(`📡 Connecting to ${network} blockchain...`)
      account = await wdk.getAccount('ethereum', 0)
    } else {
      console.log(`❌ Unsupported chain: ${chain}`)
      console.log('')
      console.log('Supported chains: testnet-bitcoin, testnet-ethereum')
      console.log('')
      await closeStorage()
      return
    }

    // Get balance
    const balance = await account.getBalance()
    const balanceNumber = Number(balance)

    console.log('')

    if (chain === 'bitcoin') {
      const networkLabel = network === 'testnet' ? 'tBTC (testnet)' : 'BTC'
      console.log(`Balance: ${(balanceNumber / 100000000).toFixed(8)} ${networkLabel}`)
      console.log(`         ${balanceNumber.toLocaleString()} satoshis`)
      console.log('')

      if (balanceNumber === 0 && network === 'testnet') {
        console.log('💡 To receive testnet Bitcoin:')
        console.log('   1. Get your address: bare hyper-wallet-autobase.cjs address current testnet-bitcoin')
        console.log('   2. Visit faucet: https://testnet-faucet.mempool.co/')
        console.log('   3. Sync: bare hyper-wallet-autobase.cjs sync testnet-bitcoin')
        console.log('')
      }
    } else if (chain === 'ethereum') {
      // Balance is in wei (10^18 wei = 1 ETH)
      const ethBalance = balanceNumber / 1e18
      const networkLabel = network === 'testnet' ? 'SepoliaETH (testnet)' : 'ETH'
      console.log(`Balance: ${ethBalance.toFixed(18).replace(/\.?0+$/, '')} ${networkLabel}`)
      console.log(`         ${balanceNumber.toLocaleString()} wei`)
      console.log('')

      if (balanceNumber === 0 && network === 'testnet') {
        console.log('💡 To receive testnet Sepolia ETH:')
        console.log('   1. Get your address: bare hyper-wallet-autobase.cjs address current testnet-ethereum')
        console.log('   2. Visit faucet: https://sepolia-faucet.pk910.de/')
        console.log('   3. Check balance: bare hyper-wallet-autobase.cjs balance testnet-ethereum')
        console.log('')
      }
    }

    // Clean up WDK resources
    wdk.dispose()
  } catch (err) {
    console.log('')
    console.log('❌ Error getting balance:', err.message)
    console.log('')
  }

  await closeStorage()
}

async function cmdSync(args) {
  const chainArg = args[1] || 'testnet-bitcoin'

  // Parse chain argument
  let chain, network
  if (chainArg.startsWith('testnet-')) {
    network = 'testnet'
    chain = chainArg.replace('testnet-', '')
  } else if (chainArg.startsWith('mainnet-')) {
    network = 'mainnet'
    chain = chainArg.replace('mainnet-', '')
  } else {
    // Legacy support: assume testnet
    network = 'testnet'
    chain = chainArg
  }

  console.log('🔄 Syncing Balance')
  console.log('')
  console.log(`Chain: ${chainArg}`)
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  try {
    // Initialize WDK
    const walletMnemonic = loadWalletMnemonic()
    if (!walletMnemonic) {
      console.log('❌ No wallet mnemonic found!')
      console.log('')
      console.log('Initialize WDK first:')
      console.log('  bare hyper-wallet-autobase.cjs wallet init')
      console.log('')
      await closeStorage()
      return
    }

    const { wdk } = await initializeWDK(walletMnemonic, { testnet: network === 'testnet' })

    // Get account for chain
    let account
    console.log(`📡 Connecting to ${network} blockchain...`)

    if (chain === 'bitcoin') {
      account = await wdk.getAccount('bitcoin', 0)
    } else if (chain === 'ethereum') {
      account = await wdk.getAccount('ethereum', 0)
    } else {
      console.log(`❌ Unsupported chain: ${chain}`)
      console.log('')
      console.log('Supported chains: testnet-bitcoin, testnet-ethereum')
      console.log('')
      await closeStorage()
      return
    }

    console.log('✅ Connected to blockchain')
    console.log('')

    // Get balance from blockchain
    console.log('Querying balance...')
    const balance = await account.getBalance()
    const balanceNumber = Number(balance)

    console.log('')

    if (chain === 'bitcoin') {
      const networkLabel = network === 'testnet' ? 'tBTC (testnet)' : 'BTC'
      console.log(`✅ Balance: ${(balanceNumber / 100000000).toFixed(8)} ${networkLabel} (${balanceNumber.toLocaleString()} sats)`)
      console.log('')

      if (balanceNumber === 0 && network === 'testnet') {
        console.log('No transactions found.')
        console.log('')
        console.log('💡 To receive testnet Bitcoin:')
        console.log('   1. Your address: tb1qfej77dh7cnxj3zxpxlj0hksxhjgpfyg7ajgvvd')
        console.log('   2. Visit faucet: https://testnet-faucet.mempool.co/')
        console.log('   3. Wait 1-2 minutes and run: bare hyper-wallet-autobase.cjs sync testnet-bitcoin')
        console.log('')
      } else if (balanceNumber > 0) {
        console.log('✅ Wallet has balance! Ready for transactions.')
        console.log('💡 WDK manages UTXOs internally for sending.')
        console.log('')
      }
    } else if (chain === 'ethereum') {
      const ethBalance = balanceNumber / 1e18
      const networkLabel = network === 'testnet' ? 'SepoliaETH (testnet)' : 'ETH'
      console.log(`✅ Balance: ${ethBalance.toFixed(18).replace(/\.?0+$/, '')} ${networkLabel} (${balanceNumber.toLocaleString()} wei)`)
      console.log('')

      if (balanceNumber === 0 && network === 'testnet') {
        console.log('No transactions found.')
        console.log('')
        console.log('💡 To receive testnet Sepolia ETH:')
        console.log('   1. Your address: 0xB91cc8b6803f55c2e1D2B56623790615C9951340')
        console.log('   2. Visit faucet: https://sepolia-faucet.pk910.de/')
        console.log('   3. Wait 1-2 minutes and run: bare hyper-wallet-autobase.cjs sync testnet-ethereum')
        console.log('')
      } else if (balanceNumber > 0) {
        console.log('✅ Wallet has balance! Ready for transactions.')
        console.log('💡 WDK manages nonce and gas internally for sending.')
        console.log('')
      }
    }

    // Clean up WDK resources
    wdk.dispose()
  } catch (err) {
    console.log('')
    console.log('❌ Error syncing UTXOs:', err.message)
    console.log('')
  }

  await closeStorage()
}

// ============================================================================
// SEND COMMAND (Phase 6.4/6.5)
// ============================================================================

async function cmdSend(args) {
  const chainArg = args[1]
  const toAddress = args[2]
  const amountArg = args[3]

  if (!chainArg || !toAddress || !amountArg) {
    console.error('❌ Missing arguments')
    console.error('')
    console.error('Usage: bare hyper-wallet-autobase.cjs send <chain> <address> <amount>')
    console.error('Example: bare hyper-wallet-autobase.cjs send testnet-bitcoin tb1q... 0.0001')
    console.error('         bare hyper-wallet-autobase.cjs send testnet-ethereum 0x... 0.01')
    console.error('')
    process.exit(1)
  }

  // Parse chain argument
  let chain, network, testnet4 = false
  if (chainArg.startsWith('testnet4-')) {
    network = 'testnet'
    testnet4 = true
    chain = chainArg.replace('testnet4-', '')
  } else if (chainArg.startsWith('testnet-')) {
    network = 'testnet'
    chain = chainArg.replace('testnet-', '')
  } else if (chainArg.startsWith('mainnet-')) {
    network = 'mainnet'
    chain = chainArg.replace('mainnet-', '')
  } else {
    // Legacy support: assume testnet
    network = 'testnet'
    chain = chainArg
  }

  console.log('💸 Send Transaction')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  try {
    // Initialize WDK
    const walletMnemonic = loadWalletMnemonic()
    if (!walletMnemonic) {
      console.log('❌ No wallet mnemonic found!')
      console.log('')
      console.log('Initialize WDK first:')
      console.log('  bare hyper-wallet-autobase.cjs wallet init')
      console.log('')
      await closeStorage()
      return
    }

    const { wdk } = await initializeWDK(walletMnemonic, { testnet: network === 'testnet', testnet4 })

    // Get account for chain
    let account, value, unit
    console.log(`📡 Connecting to ${network} blockchain...`)

    if (chain === 'bitcoin') {
      account = await wdk.getAccount('bitcoin', 0)
      // Convert BTC to satoshis
      value = BigInt(Math.round(parseFloat(amountArg) * 100000000))
      unit = 'BTC'
    } else if (chain === 'ethereum') {
      account = await wdk.getAccount('ethereum', 0)
      // Convert ETH to wei
      value = BigInt(Math.round(parseFloat(amountArg) * 1e18))
      unit = 'ETH'
    } else {
      console.log(`❌ Unsupported chain: ${chain}`)
      console.log('')
      await closeStorage()
      return
    }

    console.log('✅ Connected')
    console.log('')

    // Show transaction details
    console.log('Transaction Details:')
    console.log(`  To:     ${toAddress}`)
    console.log(`  Amount: ${amountArg} ${unit}`)
    console.log(`  Chain:  ${chainArg}`)
    console.log('')

    // Get our address (from address)
    const fromAddress = await account.getAddress()

    // Send transaction (WDK handles UTXO selection, fee, change, signing, broadcasting)
    console.log('Building and broadcasting transaction...')
    const result = await account.sendTransaction({
      to: toAddress,
      value
    })

    // Store transaction in Autobase for multi-device sync (Phase 6.6)
    await storeTransaction({
      hash: result.hash,
      chain: chain,
      network: network, // Add network (testnet/mainnet)
      direction: 'sent',
      from: fromAddress,
      to: toAddress,
      amount: value,
      fee: result.fee,
      status: 'pending',
      timestamp: Date.now()
    })

    console.log('')
    console.log('✅ Transaction sent successfully!')
    console.log('')
    console.log(`📋 Transaction ID: ${result.hash}`)
    console.log(`💰 Fee: ${Number(result.fee).toLocaleString()} ${chain === 'bitcoin' ? 'satoshis' : 'wei'}`)
    console.log('')
    console.log('🔍 View transaction:')
    if (chain === 'bitcoin') {
      console.log(`   https://blockstream.info/testnet/tx/${result.hash}`)
    } else if (chain === 'ethereum') {
      console.log(`   https://sepolia.etherscan.io/tx/${result.hash}`)
    }
    console.log('')
    console.log('📝 Transaction stored and synced across devices')
    console.log('')

    // Clean up WDK resources
    wdk.dispose()
  } catch (err) {
    console.log('')
    console.log('❌ Error sending transaction:', err.message)
    console.log('')
    if (err.message.includes('Insufficient')) {
      console.log('💡 Check your balance:')
      console.log(`   bare hyper-wallet-autobase.cjs balance ${chainArg}`)
      console.log('')
    }
  }

  await closeStorage()
}

// ============================================================================
// HISTORY COMMAND (Phase 6.6)
// ============================================================================

async function cmdHistory(args) {
  const chainArg = args[1] || 'all'

  console.log('📜 Transaction History')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  try {
    // Parse chain argument
    let chain = chainArg
    if (chainArg.startsWith('testnet-') || chainArg.startsWith('mainnet-')) {
      chain = chainArg.replace(/^(testnet|mainnet)-/, '')
    }

    const transactions = await getTransactions(chain)

    if (transactions.length === 0) {
      console.log(`No transactions found for ${chainArg}.`)
      console.log('')
      console.log('💡 Transactions are automatically stored when you send coins.')
      console.log('')
      await closeStorage()
      return
    }

    console.log(`Found ${transactions.length} transaction${transactions.length === 1 ? '' : 's'}:`)
    console.log('')

    for (const tx of transactions) {
      const date = new Date(tx.timestamp).toLocaleString()
      const icon = tx.direction === 'sent' ? '📤' : '📥'
      const status = tx.status === 'confirmed' ? '✅' : '⏳'

      console.log(`${icon} ${status} ${tx.direction.toUpperCase()} - ${tx.chain}`)
      console.log(`   TX:     ${tx.hash}`)
      console.log(`   From:   ${tx.from}`)
      console.log(`   To:     ${tx.to}`)

      if (tx.chain === 'bitcoin') {
        const btc = Number(tx.amount) / 100000000
        const feeSats = Number(tx.fee)
        console.log(`   Amount: ${btc.toFixed(8)} BTC`)
        console.log(`   Fee:    ${feeSats.toLocaleString()} satoshis`)
      } else if (tx.chain === 'ethereum') {
        const eth = Number(tx.amount) / 1e18
        const feeWei = Number(tx.fee)
        console.log(`   Amount: ${eth.toFixed(18).replace(/\.?0+$/, '')} ETH`)
        console.log(`   Fee:    ${feeWei.toLocaleString()} wei`)
      }

      console.log(`   Date:   ${date}`)
      console.log(`   Status: ${tx.status}`)
      if (tx.blockHeight) {
        console.log(`   Block:  ${tx.blockHeight}`)
      }
      console.log('')
    }

    console.log(`Total: ${transactions.length} transaction${transactions.length === 1 ? '' : 's'}`)
    console.log('')
  } catch (err) {
    console.log('')
    console.log('❌ Error loading transaction history:', err.message)
    console.log('')
  }

  await closeStorage()
}

/**
 * Sync transaction history from blockchain (Phase 6.8)
 * Uses WDK's getTransfers() to detect ALL transactions (sent + received)
 */
async function cmdSyncHistory(args) {
  const chainArg = args[1] || 'all'

  console.log('🔄 Sync Transaction History')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  // Load configuration
  const { getConfig } = require('./lib/hyper-wallet-lib/config.cjs')
  const { fetchTransactions } = require('./lib/hyper-wallet-lib/blockchain-providers.cjs')
  const config = getConfig()

  try {
    // Parse chain argument
    let chains = []
    let network = config.network // Use config network by default

    if (chainArg === 'all') {
      chains = ['bitcoin', 'ethereum']
    } else if (chainArg.startsWith('testnet-') || chainArg.startsWith('testnet4-') || chainArg.startsWith('mainnet-')) {
      // Override network if explicitly specified in argument
      if (chainArg.startsWith('testnet4-')) {
        network = 'testnet4'
        chains = [chainArg.replace('testnet4-', '')]
      } else if (chainArg.startsWith('testnet-')) {
        network = 'testnet'
        chains = [chainArg.replace('testnet-', '')]
      } else {
        network = 'mainnet'
        chains = [chainArg.replace('mainnet-', '')]
      }
    } else {
      chains = [chainArg]
    }

    // Load wallet mnemonic
    const walletMnemonic = loadWalletMnemonic()
    if (!walletMnemonic) {
      console.error('❌ No wallet found. Run "wallet init" first.')
      console.error('')
      await closeStorage()
      process.exit(1)
    }

    // Initialize WDK
    console.log(`📡 Connecting to ${network} blockchain...`)
    const { wdk } = await initializeWDK(walletMnemonic, {
      testnet: network === 'testnet' || network === 'testnet4',
      testnet4: network === 'testnet4'
    })
    console.log('✅ Connected')
    console.log('')

    let totalNew = 0
    let totalUpdated = 0

    for (const chain of chains) {
      if (chain !== 'bitcoin' && chain !== 'ethereum') {
        console.log(`⚠️  Skipping unsupported chain: ${chain}`)
        continue
      }

      console.log(`🔍 Syncing ${chain} transactions...`)
      console.log(`   Provider: ${config.blockchain[chain].provider}`)

      try {
        const account = await wdk.getAccount(chain, 0)
        const myAddress = await account.getAddress()

        let transfers = []

        // Use blockchain provider system with automatic fallback
        const result = await fetchTransactions(chain, myAddress, network, config)

        if (result.useNativeWDK) {
          // Use WDK's built-in method (Bitcoin Electrum)
          console.log(`   Using WDK native method (${result.provider})...`)
          transfers = await account.getTransfers({
            direction: 'all',
            limit: 100
          })
        } else {
          // Got transactions from HTTP API
          // Convert to WDK-compatible format
          transfers = result.map(tx => ({
            txid: tx.hash,
            address: myAddress,
            height: tx.blockNumber || 0,
            value: tx.value,
            direction: tx.to && tx.to.toLowerCase() === myAddress.toLowerCase() ? 'incoming' : 'outgoing',
            fee: tx.fee || '0',
            recipient: tx.to,
            from: tx.from
          }))
        }

        console.log(`   Found ${transfers.length} transfer${transfers.length === 1 ? '' : 's'} on blockchain`)

        for (const transfer of transfers) {
          try {
            // Skip transfers without value (malformed data)
            if (transfer.value === null || transfer.value === undefined) {
              console.log(`   ⚠️  Skipping transfer with no value: ${transfer.txid.substring(0, 16)}...`)
              continue
            }

            // Check if we already have this transaction
            const existing = await getTransaction(chain, transfer.txid)

            if (!existing) {
              // New transaction - store it
              const isIncoming = transfer.direction === 'incoming'

              // Get recipient address for outgoing transfers
              const toAddress = isIncoming ? myAddress : (transfer.recipient || '(unknown)')

              await storeTransaction({
                hash: transfer.txid,
                chain: chain,
                network: network, // Add network (testnet/mainnet)
                direction: isIncoming ? 'received' : 'sent',
                from: isIncoming ? '(external)' : myAddress,
                to: toAddress,
                amount: String(transfer.value || 0),  // Convert to string (BigInts can't be JSON stringified)
                fee: String(transfer.fee || 0),
                status: transfer.height > 0 ? 'confirmed' : 'pending',
                timestamp: Date.now(),
                blockHeight: transfer.height || null
              })

              totalNew++
              console.log(`   ✅ New ${transfer.direction} transaction: ${transfer.txid.substring(0, 16)}...`)

            } else if (existing.status === 'pending' && transfer.height > 0) {
              // Existing pending transaction got confirmed - update it
              await updateTransactionStatus(chain, transfer.txid, {
                status: 'confirmed',
                blockHeight: transfer.height
              })

              totalUpdated++
              console.log(`   ✅ Transaction confirmed: ${transfer.txid.substring(0, 16)}... (block ${transfer.height})`)
            }
          } catch (err) {
            console.log(`   ⚠️  Error processing ${transfer.txid.substring(0, 16)}...: ${String(err.message || err)}`)
          }
        }

        console.log('')
      } catch (err) {
        console.log(`   ❌ Error syncing ${chain}: ${err.message}`)
        console.log('')
      }
    }

    wdk.dispose()

    console.log('📊 Summary:')
    console.log(`   New transactions: ${totalNew}`)
    console.log(`   Updated transactions: ${totalUpdated}`)
    console.log('')

    if (totalNew > 0 || totalUpdated > 0) {
      console.log('💡 Run "history" command to view updated transaction list')
      console.log('')
    }

  } catch (err) {
    console.log('')
    console.log('❌ Error syncing history:', err.message)
    console.log('')
  }

  await closeStorage()
}

/**
 * Monitor and update transaction statuses (Phase 6.8)
 * Checks pending transactions and updates them when confirmed
 * Now uses WDK's getTransfers() API for actual status checks
 */
async function cmdMonitor(args) {
  const chainArg = args[1] || 'all'

  console.log('🔍 Transaction Status Monitor')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  try {
    // Parse chain argument
    let chain = chainArg
    let network = 'testnet'
    if (chainArg.startsWith('testnet-') || chainArg.startsWith('mainnet-')) {
      network = chainArg.startsWith('testnet-') ? 'testnet' : 'mainnet'
      chain = chainArg.replace(/^(testnet|mainnet)-/, '')
    }

    // Load all transactions
    const transactions = await getTransactions(chain)
    const pendingTransactions = transactions.filter(tx => tx.status === 'pending')

    if (pendingTransactions.length === 0) {
      console.log(`No pending transactions for ${chainArg}.`)
      console.log('')
      await closeStorage()
      return
    }

    console.log(`Found ${pendingTransactions.length} pending transaction${pendingTransactions.length === 1 ? '' : 's'}`)
    console.log('')

    // Load wallet mnemonic for WDK access
    const walletMnemonic = loadWalletMnemonic()
    if (!walletMnemonic) {
      console.error('❌ No wallet found. Run "wallet init" first.')
      console.error('')
      await closeStorage()
      process.exit(1)
    }

    // Initialize WDK
    const { wdk } = await initializeWDK(walletMnemonic, { testnet: network === 'testnet' })

    let updatedCount = 0

    // Check each chain that has pending transactions
    const chainsToCheck = chain === 'all'
      ? [...new Set(pendingTransactions.map(tx => tx.chain))]
      : [chain]

    for (const checkChain of chainsToCheck) {
      try {
        const account = await wdk.getAccount(checkChain, 0)

        // Get all transfers to check status
        const transfers = await account.getTransfers({ direction: 'all', limit: 100 })

        // Build a map of txid -> transfer for quick lookup
        const transferMap = new Map()
        for (const transfer of transfers) {
          transferMap.set(transfer.txid, transfer)
        }

        // Check each pending transaction
        const chainPending = pendingTransactions.filter(tx => tx.chain === checkChain)

        for (const tx of chainPending) {
          const transfer = transferMap.get(tx.hash)

          if (transfer && transfer.height > 0) {
            // Transaction is confirmed!
            await updateTransactionStatus(checkChain, tx.hash, {
              status: 'confirmed',
              blockHeight: transfer.height
            })

            updatedCount++
            console.log(`✅ ${tx.hash.substring(0, 16)}... confirmed at block ${transfer.height}`)
          } else if (transfer) {
            console.log(`⏳ ${tx.hash.substring(0, 16)}... still pending`)
          } else {
            console.log(`❓ ${tx.hash.substring(0, 16)}... not found in transfers (may take time to appear)`)
          }
        }

      } catch (err) {
        console.log(`❌ Error checking ${checkChain}: ${err.message}`)
      }
    }

    console.log('')
    wdk.dispose()

    if (updatedCount > 0) {
      console.log(`✅ Updated ${updatedCount} transaction${updatedCount === 1 ? '' : 's'} to confirmed`)
      console.log('')
    } else {
      console.log('💡 No status changes detected.')
      console.log('')
    }

  } catch (err) {
    console.log('')
    console.log('❌ Error monitoring transactions:', err.message)
    console.log('')
  }

  await closeStorage()
}

/**
 * Background daemon for automatic transaction sync (Phase 6.9)
 * Runs sync-history periodically to detect new transactions
 */
async function cmdDaemon(args) {
  const subcommand = args[1]
  const DAEMON_PID_FILE = path.join(os.homedir(), '.hyper-wallet', 'daemon.pid')
  const DAEMON_LOG_FILE = path.join(os.homedir(), '.hyper-wallet', 'daemon.log')
  const DAEMON_INTERVAL = 5 * 60 * 1000 // 5 minutes

  if (subcommand === 'start') {
    // Check if already running
    if (fs.existsSync(DAEMON_PID_FILE)) {
      const pid = fs.readFileSync(DAEMON_PID_FILE, 'utf8').trim()
      console.log(`⚠️  Daemon already running with PID ${pid}`)
      console.log('   Run "daemon stop" first to restart')
      console.log('')
      return
    }

    console.log('🚀 Starting transaction sync daemon')
    console.log(`   Sync interval: ${DAEMON_INTERVAL / 1000 / 60} minutes`)
    console.log(`   Log file: ${DAEMON_LOG_FILE}`)
    console.log('')

    // Fork a background process
    const childProcess = require('bare-subprocess')
    const daemon = childProcess.spawn(process.argv[0], [
      process.argv[1],
      'daemon',
      'run'
    ], {
      detached: true,
      stdio: 'ignore'
    })

    daemon.unref()

    // Save PID
    const dir = path.dirname(DAEMON_PID_FILE)
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true, mode: 0o700 })
    }
    fs.writeFileSync(DAEMON_PID_FILE, daemon.pid.toString())

    console.log(`✅ Daemon started with PID ${daemon.pid}`)
    console.log('')
    console.log('💡 Commands:')
    console.log('   bare hyper-wallet-autobase.cjs daemon status  - Check status')
    console.log('   bare hyper-wallet-autobase.cjs daemon stop    - Stop daemon')
    console.log('   tail -f ' + DAEMON_LOG_FILE + ' - View logs')
    console.log('')

  } else if (subcommand === 'stop') {
    if (!fs.existsSync(DAEMON_PID_FILE)) {
      console.log('⚠️  Daemon is not running')
      console.log('')
      return
    }

    const pid = parseInt(fs.readFileSync(DAEMON_PID_FILE, 'utf8').trim())
    console.log(`🛑 Stopping daemon (PID ${pid})...`)

    try {
      process.kill(pid, 'SIGTERM')
      fs.unlinkSync(DAEMON_PID_FILE)
      console.log('✅ Daemon stopped')
      console.log('')
    } catch (err) {
      console.log(`❌ Error stopping daemon: ${err.message}`)
      console.log('   Process may have already died. Cleaning up PID file...')
      fs.unlinkSync(DAEMON_PID_FILE)
      console.log('')
    }

  } else if (subcommand === 'status') {
    if (!fs.existsSync(DAEMON_PID_FILE)) {
      console.log('⚪ Daemon is not running')
      console.log('')
      console.log('💡 Start with: bare hyper-wallet-autobase.cjs daemon start')
      console.log('')
      return
    }

    const pid = parseInt(fs.readFileSync(DAEMON_PID_FILE, 'utf8').trim())

    // Check if process is actually running
    try {
      process.kill(pid, 0) // Signal 0 checks if process exists
      console.log('🟢 Daemon is running')
      console.log(`   PID: ${pid}`)
      console.log(`   Log: ${DAEMON_LOG_FILE}`)
      console.log('')
      console.log('💡 View logs: tail -f ' + DAEMON_LOG_FILE)
      console.log('')
    } catch (err) {
      console.log('🔴 Daemon PID file exists but process is not running')
      console.log(`   Stale PID: ${pid}`)
      console.log('   Cleaning up...')
      fs.unlinkSync(DAEMON_PID_FILE)
      console.log('✅ Cleaned up stale PID file')
      console.log('')
    }

  } else if (subcommand === 'run') {
    // This is the actual daemon loop (runs in background)
    const logStream = fs.createWriteStream(DAEMON_LOG_FILE, { flags: 'a' })

    function log(message) {
      const timestamp = new Date().toISOString()
      const line = `[${timestamp}] ${message}\n`
      logStream.write(line)
    }

    log('Daemon started')
    log(`Sync interval: ${DAEMON_INTERVAL / 1000 / 60} minutes`)

    async function syncLoop() {
      try {
        log('Running sync-history...')

        // Load identity
        const identity = await loadIdentity()
        await initializeStorage(identity)

        // Load wallet mnemonic
        const walletMnemonic = loadWalletMnemonic()
        if (!walletMnemonic) {
          log('ERROR: No wallet found')
          return
        }

        // Initialize WDK
        const { wdk } = await initializeWDK(walletMnemonic, { testnet: true })

        let totalNew = 0
        let totalUpdated = 0

        // Sync Bitcoin
        try {
          const account = await wdk.getAccount('bitcoin', 0)
          const myAddress = await account.getAddress()
          const transfers = await account.getTransfers({ direction: 'all', limit: 100 })

          for (const transfer of transfers) {
            if (transfer.value === null || transfer.value === undefined) continue

            const existing = await getTransaction('bitcoin', transfer.txid)

            if (!existing) {
              const isIncoming = transfer.direction === 'incoming'
              const toAddress = isIncoming ? myAddress : (transfer.recipient || '(unknown)')

              await storeTransaction({
                hash: transfer.txid,
                chain: 'bitcoin',
                network: 'testnet', // Daemon is hardcoded to testnet for now
                direction: isIncoming ? 'received' : 'sent',
                from: isIncoming ? '(external)' : myAddress,
                to: toAddress,
                amount: Number(transfer.value),
                fee: Number(transfer.fee || 0n),
                status: transfer.height > 0 ? 'confirmed' : 'pending',
                timestamp: Date.now(),
                blockHeight: transfer.height || null
              })

              totalNew++
              log(`New ${transfer.direction} transaction: ${transfer.txid.substring(0, 16)}...`)

            } else if (existing.status === 'pending' && transfer.height > 0) {
              await updateTransactionStatus('bitcoin', transfer.txid, {
                status: 'confirmed',
                blockHeight: transfer.height
              })

              totalUpdated++
              log(`Transaction confirmed: ${transfer.txid.substring(0, 16)}... (block ${transfer.height})`)
            }
          }
        } catch (err) {
          log(`ERROR syncing Bitcoin: ${err.message}`)
        }

        wdk.dispose()
        await closeStorage()

        log(`Sync complete: ${totalNew} new, ${totalUpdated} updated`)

      } catch (err) {
        log(`ERROR in sync loop: ${err.message}`)
      }
    }

    // Run immediately on start
    syncLoop()

    // Then run every DAEMON_INTERVAL
    setInterval(syncLoop, DAEMON_INTERVAL)

    // Keep process alive
    process.on('SIGTERM', () => {
      log('Received SIGTERM, shutting down...')
      logStream.end()
      process.exit(0)
    })

  } else {
    console.error(`❌ Unknown daemon command: ${subcommand}`)
    console.error('')
    console.error('Available: start, stop, status')
    console.error('')
    process.exit(1)
  }
}

/**
 * Settings management (Phase 6.9)
 * Manage blockchain providers and configuration
 */
async function cmdSettings(args) {
  const { getConfig, updateConfig, saveConfig, DEFAULT_CONFIG } = require('./lib/hyper-wallet-lib/config.cjs')
  const { getAvailableProviders, getProviderInfo } = require('./lib/hyper-wallet-lib/blockchain-providers.cjs')

  const subcommand = args[1]

  if (!subcommand || subcommand === 'show') {
    // Display current configuration
    const config = getConfig()

    console.log('⚙️  Wallet Settings')
    console.log('')
    console.log('📡 Network')
    console.log(`   ${config.network}`)
    console.log('')

    console.log('🔗 Blockchain Providers')
    console.log(`   Bitcoin:  ${config.blockchain.bitcoin.provider}`)
    console.log(`   Ethereum: ${config.blockchain.ethereum.provider}`)
    if (config.blockchain.ethereum.apiKey) {
      const masked = '***' + config.blockchain.ethereum.apiKey.slice(-4)
      console.log(`   API Key:  ${masked}`)
    }
    console.log('')

    console.log('🎨 UI Settings')
    console.log(`   Theme:     ${config.ui.theme}`)
    console.log(`   Auto Lock: ${config.ui.autoLock} minutes`)
    console.log(`   Currency:  ${config.ui.currency}`)
    console.log('')

    console.log('🔄 Sync Settings')
    console.log(`   Auto Sync:     ${config.sync.autoSync ? 'enabled' : 'disabled'}`)
    console.log(`   Sync Interval: ${config.sync.syncInterval} minutes`)
    console.log('')

    console.log('🔒 Security')
    console.log(`   Require Password on Send: ${config.security.requirePasswordOnSend ? 'yes' : 'no'}`)
    console.log(`   Session Timeout:          ${config.security.sessionTimeout} minutes`)
    console.log('')

    console.log('💡 Commands:')
    console.log('   settings set <key> <value>  - Update a setting')
    console.log('   settings providers          - List available providers')
    console.log('   settings reset              - Reset to defaults')
    console.log('')

  } else if (subcommand === 'set') {
    // Update configuration value
    const key = args[2]
    const value = args[3]

    if (!key || value === undefined) {
      console.error('❌ Usage: settings set <key> <value>')
      console.error('')
      console.error('Examples:')
      console.error('   settings set blockchain.ethereum.provider etherscan')
      console.error('   settings set blockchain.ethereum.apiKey YOUR_API_KEY')
      console.error('   settings set network mainnet')
      console.error('   settings set ui.theme light')
      console.error('')
      process.exit(1)
    }

    // Parse value (handle booleans and numbers)
    let parsedValue = value
    if (value === 'true') parsedValue = true
    else if (value === 'false') parsedValue = false
    else if (value === 'null') parsedValue = null
    else if (!isNaN(value)) parsedValue = Number(value)

    try {
      updateConfig(key, parsedValue)
      console.log('✅ Setting updated')
      console.log(`   ${key} = ${parsedValue}`)
      console.log('')
      console.log('💡 View settings: bare hyper-wallet-autobase.cjs settings show')
      console.log('')
    } catch (err) {
      console.error(`❌ Error updating setting: ${err.message}`)
      console.error('')
      process.exit(1)
    }

  } else if (subcommand === 'providers') {
    // List available blockchain providers
    console.log('📡 Available Blockchain Providers')
    console.log('')

    console.log('Ethereum:')
    const ethProviders = getAvailableProviders('ethereum')
    for (const name of ethProviders) {
      const info = getProviderInfo('ethereum', name)
      const apiKeyStatus = info.needsApiKey ? '🔑 Requires API key' : '✅ No API key needed'
      console.log(`   ${name.padEnd(12)} - ${info.name} (${apiKeyStatus})`)
    }
    console.log('')

    console.log('Bitcoin:')
    const btcProviders = getAvailableProviders('bitcoin')
    for (const name of btcProviders) {
      const info = getProviderInfo('bitcoin', name)
      if (info.usesNativeWDK) {
        console.log(`   ${name.padEnd(12)} - ${info.name} (✅ Native WDK)`)
      } else {
        const apiKeyStatus = info.needsApiKey ? '🔑 Requires API key' : '✅ No API key needed'
        console.log(`   ${name.padEnd(12)} - ${info.name} (${apiKeyStatus})`)
      }
    }
    console.log('')

    console.log('💡 Change provider:')
    console.log('   bare hyper-wallet-autobase.cjs settings set blockchain.ethereum.provider blockscout')
    console.log('')

  } else if (subcommand === 'reset') {
    // Reset to defaults
    console.log('⚠️  This will reset all settings to defaults')
    console.log('   Current settings will be lost')
    console.log('')
    console.log('💡 To confirm, run:')
    console.log('   bare hyper-wallet-autobase.cjs settings reset-confirm')
    console.log('')

  } else if (subcommand === 'reset-confirm') {
    // Actually reset (requires confirmation)
    saveConfig(DEFAULT_CONFIG)
    console.log('✅ Settings reset to defaults')
    console.log('')
    console.log('💡 View settings: bare hyper-wallet-autobase.cjs settings show')
    console.log('')

  } else {
    console.error(`❌ Unknown settings command: ${subcommand}`)
    console.error('')
    console.error('Available commands:')
    console.error('   show      - Display current settings')
    console.error('   set       - Update a setting')
    console.error('   providers - List available blockchain providers')
    console.error('   reset     - Reset to defaults')
    console.error('')
    process.exit(1)
  }
}

async function cmdUTXOList(args) {
  const chain = args[2] || 'bitcoin'

  console.log('📋 UTXO List')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  try {
    const utxos = await getUTXOs(chain)

    if (utxos.length === 0) {
      console.log(`No UTXOs found for ${chain}.`)
      console.log('')
      console.log('Sync UTXOs from blockchain:')
      console.log(`  bare hyper-wallet-autobase.cjs sync ${chain}`)
      console.log('')
      await closeStorage()
      return
    }

    // Group by status
    const spendable = utxos.filter(u => !u.spent && u.spendable)
    const spent = utxos.filter(u => u.spent)

    if (spendable.length > 0) {
      console.log(`💎 Spendable (${spendable.length})`)
      console.log('')

      for (const utxo of spendable) {
        const confirmIcon = utxo.confirmations >= 6 ? '✅' : '⏳'
        const btcAmount = (utxo.amount / 100000000).toFixed(8)

        console.log(`${confirmIcon} ${btcAmount} BTC (${utxo.amount.toLocaleString()} sats)`)
        console.log(`   TXID: ${utxo.txid}:${utxo.vout}`)
        console.log(`   Address: ${utxo.address}`)
        console.log(`   Confirmations: ${utxo.confirmations}`)
        console.log('')
      }
    }

    if (spent.length > 0) {
      console.log(`❌ Spent (${spent.length})`)
      console.log('')

      for (const utxo of spent) {
        const btcAmount = (utxo.amount / 100000000).toFixed(8)

        console.log(`   ${btcAmount} BTC`)
        console.log(`   TXID: ${utxo.txid}:${utxo.vout}`)
        console.log(`   Spent in: ${utxo.spentTxid || 'unknown'}`)
        console.log('')
      }
    }

    // Show total
    const balance = calculateBalance(utxos)
    const symbol = chain === 'bitcoin' ? 'BTC' : 'ETH'
    console.log('─────────────────────────────────────')
    console.log(formatBalance(balance, symbol))
    console.log('')
  } catch (err) {
    console.log('')
    console.log('❌ Error listing UTXOs:', err.message)
    console.log('')
  }

  await closeStorage()
}

// ============================================================================
// DEVICE COMMANDS
// ============================================================================

async function cmdDeviceBootstrap(args) {
  const deviceName = args[2] || 'Default Device'

  console.log('🖥️  Bootstrapping Device')
  console.log('')
  console.log(`Device Name: ${deviceName}`)
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  const device = await bootstrapDevice(identity, deviceName)

  console.log('✅ Device bootstrapped!')
  console.log('')
  console.log(`Name:   ${device.name}`)
  console.log(`Writer: ${b4a.toString(device.key, 'hex').slice(0, 16)}...`)
  console.log(`Added:  ${new Date(device.addedAt).toLocaleString()}`)
  console.log('')
  console.log('This device can now:')
  console.log('  - Create invites for other devices')
  console.log('  - Participate in multi-device sync')
  console.log('')

  await closeStorage()
}

async function cmdDeviceAdd(args) {
  console.log('📱 Adding New Device')
  console.log('')

  const identity = await loadIdentity()
  const identityMnemonic = loadMnemonic()
  const walletMnemonic = loadWalletMnemonic()

  if (!walletMnemonic) {
    console.log('❌ Wallet mnemonic not found!')
    console.log('')
    console.log('Run: bare hyper-wallet-autobase.cjs init')
    console.log('')
    process.exit(1)
  }

  await initializeStorage(identity)

  const invite = await createInvite(identity)

  console.log('✅ Invite created!')
  console.log('')
  console.log('Share this invite code with your other device:')
  console.log('')
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
  console.log(invite)
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
  console.log('')
  console.log('On the other device, run:')
  console.log(`bare hyper-wallet-autobase.cjs device join "<invite>" "Device Name"`)
  console.log('')
  console.log('📡 Waiting for device to connect...')
  console.log('   (Press Ctrl+C to cancel)')
  console.log('')

  try {
    // Wait indefinitely for device to connect, send BOTH mnemonics
    const { writerKey, deviceName } = await waitForNewDevice(0, identityMnemonic, walletMnemonic)

    console.log('')
    console.log('✅ Device added successfully!')
    console.log(`   Name:   ${deviceName}`)
    console.log(`   Writer: ${b4a.toString(writerKey, 'hex').slice(0, 16)}...`)
    console.log('')
    console.log('The new device is now part of your wallet.')
    console.log('')
  } catch (err) {
    console.log('')
    console.log('❌ Error:', err.message)
    console.log('')
  }

  await closeStorage()
}

async function cmdDeviceJoin(args) {
  const inviteCode = args[2]
  const deviceName = args[3] || 'New Device'

  if (!inviteCode) {
    console.error('❌ Missing invite code')
    console.error('')
    console.error('Usage: bare hyper-wallet-autobase.cjs device join <invite> "Device Name"')
    console.error('')
    process.exit(1)
  }

  console.log('🔗 Joining Wallet')
  console.log('')
  console.log(`Device Name: ${deviceName}`)
  console.log('')

  // Join wallet (receives BOTH mnemonics from primary device)
  const { device, identityMnemonic, walletMnemonic } = await joinWallet(inviteCode, deviceName)

  // Save both mnemonics
  saveMnemonic(identityMnemonic)
  saveWalletMnemonic(walletMnemonic)

  console.log('✅ Joined wallet successfully!')
  console.log('')
  console.log(`Name:   ${device.name}`)
  console.log(`Writer: ${b4a.toString(device.key, 'hex').slice(0, 16)}...`)
  console.log('')
  console.log('🔐 Mnemonics received and saved securely')
  console.log('   Identity: ~/.hyper-wallet/.mnemonic (24 words)')
  console.log('   Wallet:   ~/.hyper-wallet/.wallet-mnemonic (12 words)')
  console.log('')
  console.log('This device is now part of the wallet and has been approved.')
  console.log('')
  console.log('Next steps:')
  console.log('  1. Test WDK: bare hyper-wallet-autobase.cjs wallet init')
  console.log('')

  await closeStorage()
}

async function cmdDeviceList(args) {
  console.log('📱 Device List')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  const devices = listDevices()

  if (devices.length === 0) {
    console.log('No devices registered.')
    console.log('')
    console.log('Bootstrap this device first:')
    console.log('  bare hyper-wallet-autobase.cjs device bootstrap "Device Name"')
    console.log('')
    await closeStorage()
    return
  }

  const currentDevice = getCurrentDevice()

  for (const device of devices) {
    const isCurrent = currentDevice && b4a.equals(device.key, currentDevice.key)
    const icon = device.isLocal ? '🖥️ ' : '📱'
    const marker = isCurrent ? ' (this device)' : ''

    console.log(`${icon} ${device.name}${marker}`)
    console.log(`   Writer:  ${b4a.toString(device.key, 'hex').slice(0, 16)}...`)
    console.log(`   Added:   ${new Date(device.addedAt).toLocaleString()}`)
    console.log(`   Local:   ${device.isLocal}`)
    console.log('')
  }

  console.log(`Total: ${devices.length} device${devices.length === 1 ? '' : 's'}`)
  console.log('')

  await closeStorage()
}

async function cmdDeviceStatus(args) {
  console.log('📊 Device Status')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  const status = getConnectionStatus()
  const currentDevice = getCurrentDevice()

  if (currentDevice) {
    console.log('Current Device:')
    console.log(`  Name:   ${currentDevice.name}`)
    console.log(`  Writer: ${b4a.toString(currentDevice.key, 'hex').slice(0, 16)}...`)
    console.log('')
  }

  console.log('Network Status:')
  console.log(`  Connected: ${status.connected ? '✅ Yes' : '❌ No'}`)
  console.log(`  Peers:     ${status.count}`)
  console.log('')

  if (status.count > 0) {
    console.log('Active Connections:')
    for (const conn of status.connections) {
      console.log(`  📡 ${conn.remotePublicKey}`)
    }
    console.log('')
  }

  await closeStorage()
}

async function cmdDeviceSync(args) {
  console.log('🔄 Starting Multi-Device Sync')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  const currentDevice = getCurrentDevice()
  if (!currentDevice) {
    console.log('⚠️  Device not bootstrapped!')
    console.log('')
    console.log('Run: bare hyper-wallet-autobase.cjs device bootstrap "Device Name"')
    console.log('')
    await closeStorage()
    return
  }

  console.log(`Device: ${currentDevice.name}`)
  console.log('')

  // Enable replication for all connections
  enableReplication()

  console.log('📡 Listening for connections...')
  console.log('')
  console.log('Press Ctrl+C to stop syncing.')
  console.log('')

  // Setup graceful shutdown
  process.on('SIGINT', async () => {
    console.log('')
    console.log('🛑 Stopping sync...')
    await closeStorage()
    process.exit(0)
  })

  // Keep running for sync
  await new Promise(() => {})
}

async function cmdDeviceApprove(args) {
  const writerKeyHex = args[2]
  const deviceName = args[3] || 'Approved Device'

  console.log('✅ Approve Pending Writer')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  // Get pending writers
  const pending = getPendingWriters()

  if (pending.length === 0) {
    console.log('ℹ️  No pending writers found.')
    console.log('')
    console.log('Make sure the other device has run "device join" first.')
    console.log('')
    await closeStorage()
    return
  }

  console.log(`Found ${pending.length} pending writer${pending.length === 1 ? '' : 's'}:`)
  console.log('')

  for (const p of pending) {
    const keyHex = b4a.toString(p.key, 'hex')
    console.log(`📝 ${keyHex.slice(0, 16)}...`)
    console.log(`   Full key: ${keyHex}`)
    console.log(`   Has proof: ${p.proof ? 'Yes ✅' : 'No ⚠️'}`)
    console.log(`   Added: ${new Date(p.addedAt).toLocaleString()}`)
    console.log('')
  }

  // If writer key provided, approve it
  if (writerKeyHex) {
    // Find matching writer (allow partial match)
    const pendingWriter = pending.find(p => {
      const hex = b4a.toString(p.key, 'hex')
      return hex === writerKeyHex || hex.startsWith(writerKeyHex)
    })

    if (!pendingWriter) {
      console.log('❌ Writer not found in pending list')
      console.log('')
      console.log('Available writers listed above.')
      console.log('')
      await closeStorage()
      return
    }

    console.log(`Approving: ${b4a.toString(pendingWriter.key, 'hex').slice(0, 16)}...`)
    console.log('')

    await approveWriter(pendingWriter.key, deviceName)

    console.log('')
    console.log('✅ Device approved successfully!')
    console.log('')
    console.log('The device can now participate in multi-device sync.')
    console.log('')
  } else {
    console.log('To approve a writer, run:')
    console.log('  bare hyper-wallet-autobase.cjs device approve <writer-key> "Device Name"')
    console.log('')
    console.log('You can use the short form (first 16 characters) or the full key.')
    console.log('')
  }

  await closeStorage()
}

async function cmdDeviceRemove(args) {
  const deviceKeyHex = args[2]

  if (!deviceKeyHex) {
    console.error('❌ Missing device key')
    console.error('')
    console.error('Usage: bare hyper-wallet-autobase.cjs device remove <device-key>')
    console.error('')
    console.error('Get device keys from: bare hyper-wallet-autobase.cjs device list')
    console.error('')
    process.exit(1)
  }

  console.log('🗑️  Remove Device')
  console.log('')

  const identity = await loadIdentity()
  await initializeStorage(identity)

  const devices = listDevices()

  // Find matching device (allow partial match on key)
  const device = devices.find(d => {
    const keyHex = b4a.toString(d.key, 'hex')
    return keyHex === deviceKeyHex || keyHex.startsWith(deviceKeyHex)
  })

  if (!device) {
    console.log('❌ Device not found')
    console.log('')
    console.log('Available devices:')
    for (const d of devices) {
      console.log(`  ${d.name}: ${b4a.toString(d.key, 'hex').slice(0, 16)}...`)
    }
    console.log('')
    await closeStorage()
    return
  }

  // Prevent removing local device
  if (device.isLocal) {
    console.log('❌ Cannot remove local device')
    console.log('')
    console.log('You cannot remove the device you are currently using.')
    console.log('')
    await closeStorage()
    return
  }

  console.log(`Device: ${device.name}`)
  console.log(`Key:    ${b4a.toString(device.key, 'hex').slice(0, 16)}...`)
  console.log('')
  console.log('⚠️  WARNING: This will revoke write access for this device.')
  console.log('   Existing data from this device will remain.')
  console.log('')

  await removeDevice(device.key)

  console.log('✅ Device removed successfully')
  console.log('')
  console.log('The device can no longer write to the wallet.')
  console.log('It may still be able to read until restarted.')
  console.log('')

  await closeStorage()
}

// ============================================================================
// HELP
// ============================================================================

function showHelp() {
  console.log(`
Hyper Wallet - Multi-Device P2P Cryptocurrency Wallet

USAGE:
  bare hyper-wallet-autobase.cjs <command> [options]

COMMANDS:
  init              Initialize wallet identity (creates 2 mnemonics)
  whoami            Show identity and current device info

WALLET MANAGEMENT (WDK):
  wallet init       Initialize WDK and test address generation
                    (Run after 'init' to verify cryptocurrency setup)

ADDRESS MANAGEMENT (Phase 6.2):
  address new [chain]
                    Generate new receiving address
                    Chain: bitcoin (default), ethereum
                    Example: bare hyper-wallet-autobase.cjs address new bitcoin

  address list [chain]
                    List all addresses for chain
                    Chain: bitcoin, ethereum, or all (default)
                    Example: bare hyper-wallet-autobase.cjs address list all

  address current <chain>
                    Show current (latest) address for chain
                    Chain: bitcoin or ethereum (required)
                    Example: bare hyper-wallet-autobase.cjs address current bitcoin

BALANCE & UTXO MANAGEMENT (Phase 6.3):
  balance [chain]   Show balance for chain
                    Chain: bitcoin (default), ethereum
                    Example: bare hyper-wallet-autobase.cjs balance bitcoin

  sync [chain]      Sync UTXOs from blockchain
                    Chain: bitcoin (default), ethereum
                    Example: bare hyper-wallet-autobase.cjs sync bitcoin

  utxo list [chain] List all UTXOs (spendable + spent)
                    Chain: bitcoin (default), ethereum
                    Example: bare hyper-wallet-autobase.cjs utxo list bitcoin

TRANSACTION MANAGEMENT (Phase 6.4-6.8):
  send <chain> <address> <amount>
                    Send cryptocurrency to address
                    Chain: testnet-bitcoin, testnet-ethereum
                    Example: bare hyper-wallet-autobase.cjs send testnet-bitcoin tb1q... 0.0001

  history [chain]   View transaction history
                    Chain: testnet-bitcoin, testnet-ethereum, or all (default)
                    Example: bare hyper-wallet-autobase.cjs history testnet-bitcoin

  sync-history [chain]
                    Sync transaction history from blockchain (detects received transactions)
                    Uses WDK's getTransfers() API to detect ALL transactions
                    Chain: testnet-bitcoin, testnet-ethereum, or all (default)
                    Example: bare hyper-wallet-autobase.cjs sync-history all

  monitor [chain]   Check and update pending transaction statuses
                    Uses WDK's getTransfers() API to check confirmations
                    Chain: testnet-bitcoin, testnet-ethereum, or all (default)
                    Example: bare hyper-wallet-autobase.cjs monitor all

  daemon start      Start background sync daemon (runs sync-history every 5 min)
  daemon stop       Stop background sync daemon
  daemon status     Check daemon status

  settings show     Display current configuration
  settings set <key> <value>
                    Update a configuration value
                    Example: bare hyper-wallet-autobase.cjs settings set blockchain.ethereum.provider blockscout
  settings providers
                    List available blockchain providers
  settings reset    Reset all settings to defaults

DEVICE MANAGEMENT:
  device bootstrap "Device Name"
                    Bootstrap this device (first step)

  device add        Create invite for another device
                    (Automatically accepts connection)

  device join <invite> "Device Name"
                    Join wallet using invite code

  device list       List all registered devices

  device status     Show connection status

  device sync       Keep running for multi-device sync
                    (Use when you want devices to stay connected)

  device approve [writer-key] [name]
                    Approve a pending writer after device join
                    Run without args to list pending writers

  device remove <device-key>
                    Remove a device (revoke write access)
                    Use short or full device key from device list

EXAMPLES:
  # On Device A (first device):
  bare hyper-wallet-autobase.cjs init
  bare hyper-wallet-autobase.cjs device bootstrap "Tabertron"
  bare hyper-wallet-autobase.cjs wallet init
  bare hyper-wallet-autobase.cjs address new bitcoin
  bare hyper-wallet-autobase.cjs address list all

  # Sync UTXOs and check balance (Phase 6.3):
  bare hyper-wallet-autobase.cjs sync bitcoin          # Query blockchain
  bare hyper-wallet-autobase.cjs balance bitcoin       # Show balance
  bare hyper-wallet-autobase.cjs utxo list bitcoin     # List all UTXOs

  # Send transactions (Phase 6.4-6.8):
  bare hyper-wallet-autobase.cjs send testnet-bitcoin tb1q... 0.0001  # Send BTC
  bare hyper-wallet-autobase.cjs history testnet-bitcoin              # View history
  bare hyper-wallet-autobase.cjs sync-history all                     # Sync from blockchain (detects received)
  bare hyper-wallet-autobase.cjs monitor all                          # Update statuses

  # Add another device:
  bare hyper-wallet-autobase.cjs device add

  # On Device B (joining):
  bare hyper-wallet-autobase.cjs device join "<invite>" "Ashatron"
  bare hyper-wallet-autobase.cjs wallet init           # Same addresses!
  bare hyper-wallet-autobase.cjs balance bitcoin       # Same balance!
  bare hyper-wallet-autobase.cjs address list bitcoin  # Synced from Device A

  # Back on Device A (device management):
  bare hyper-wallet-autobase.cjs device approve         # List pending
  bare hyper-wallet-autobase.cjs device approve <key>   # Approve specific

STORAGE:
  ~/.hyper-wallet/.mnemonic         Identity mnemonic (24 words)
  ~/.hyper-wallet/.wallet-mnemonic  Wallet mnemonic (12 words)
  ~/.hyper-wallet/.devices          Device list
  ~/.hyper-wallet/storage/          Autobase storage (tx + addresses)

POWERED BY:
  ✅ Autobase (multi-writer CRDT)
  ✅ Hyperswarm (P2P networking)
  ✅ keet-identity-key (device attestation)
`)
}

// ============================================================================
// MAIN
// ============================================================================

async function main() {
  const args = process.argv.slice(2)
  const command = args[0]
  const subcommand = args[1]

  try {
    if (command === 'wallet') {
      // Wallet management subcommands
      switch (subcommand) {
        case 'init':
          await cmdWalletInit(args)
          break

        default:
          console.error(`❌ Unknown wallet command: ${subcommand}`)
          console.error('')
          console.error('Available: init')
          console.error('')
          process.exit(1)
      }
    } else if (command === 'address') {
      // Address management subcommands (Phase 6.2)
      switch (subcommand) {
        case 'new':
          await cmdAddressNew(args)
          break

        case 'list':
          await cmdAddressList(args)
          break

        case 'current':
          await cmdAddressCurrent(args)
          break

        default:
          console.error(`❌ Unknown address command: ${subcommand}`)
          console.error('')
          console.error('Available: new, list, current')
          console.error('')
          process.exit(1)
      }
    } else if (command === 'balance') {
      // Balance command (Phase 6.3)
      await cmdBalance(args)
    } else if (command === 'sync') {
      // Sync command (Phase 6.3)
      await cmdSync(args)
    } else if (command === 'send') {
      // Send transaction command (Phase 6.4/6.5)
      await cmdSend(args)
    } else if (command === 'history') {
      // Transaction history command (Phase 6.6)
      await cmdHistory(args)
    } else if (command === 'sync-history') {
      // Sync transaction history from blockchain (Phase 6.8)
      await cmdSyncHistory(args)
    } else if (command === 'monitor') {
      // Transaction status monitor (Phase 6.8)
      await cmdMonitor(args)
    } else if (command === 'daemon') {
      // Background sync daemon (Phase 6.9)
      await cmdDaemon(args)
    } else if (command === 'settings') {
      // Settings management
      await cmdSettings(args)
    } else if (command === 'utxo') {
      // UTXO management subcommands (Phase 6.3)
      switch (subcommand) {
        case 'list':
          await cmdUTXOList(args)
          break

        default:
          console.error(`❌ Unknown utxo command: ${subcommand}`)
          console.error('')
          console.error('Available: list')
          console.error('')
          process.exit(1)
      }
    } else if (command === 'device') {
      // Device management subcommands
      switch (subcommand) {
        case 'bootstrap':
          await cmdDeviceBootstrap(args)
          break

        case 'add':
          await cmdDeviceAdd(args)
          break

        case 'join':
          await cmdDeviceJoin(args)
          break

        case 'list':
          await cmdDeviceList(args)
          break

        case 'status':
          await cmdDeviceStatus(args)
          break

        case 'sync':
          await cmdDeviceSync(args)
          break

        case 'approve':
          await cmdDeviceApprove(args)
          break

        case 'remove':
          await cmdDeviceRemove(args)
          break

        default:
          console.error(`❌ Unknown device command: ${subcommand}`)
          console.error('')
          console.error('Available: bootstrap, add, join, list, status, sync, approve, remove')
          console.error('')
          process.exit(1)
      }
    } else {
      // Top-level commands
      switch (command) {
        case 'init':
          await cmdInit(args)
          break

        case 'whoami':
          await cmdWhoami()
          break

        case 'help':
        case '--help':
        case '-h':
        default:
          showHelp()
          break
      }
    }
  } catch (err) {
    console.error('')
    console.error('❌ Error:', err.message)
    if (process.env.DEBUG) {
      console.error('')
      console.error('Stack trace:')
      console.error(err.stack)
    }
    console.error('')
    process.exit(1)
  }
}

main()