OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-wallet / deprecated / lib / wallet-context.cjs

157 lines · javascript ref:
// lib/wallet-context.cjs - Storage layer with Corestore + Hyperbee

require('bare-node-runtime/global')
const path = require('bare-path')
const os = require('bare-os')
const Corestore = require('corestore')
const Hyperbee = require('hyperbee')

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

// Singleton instances
let store = null
let txHistoryDb = null

/**
 * Initialize Corestore and Hyperbee
 * Creates storage directory and sets up transaction history database
 */
async function initializeStorage() {
  if (store && txHistoryDb) {
    return { store, txHistoryDb }
  }

  // Initialize Corestore
  store = new Corestore(STORAGE_DIR)
  await store.ready()

  // Create Hyperbee for transaction history
  const txHistoryCore = store.get({ name: 'tx-history' })
  await txHistoryCore.ready()

  txHistoryDb = new Hyperbee(txHistoryCore, {
    keyEncoding: 'utf-8',
    valueEncoding: 'json'
  })
  await txHistoryDb.ready()

  return { store, txHistoryDb }
}

/**
 * Store a transaction in the history database
 * @param {Object} tx - Transaction details
 * @param {string} tx.hash - Transaction hash
 * @param {string} tx.chain - Chain ('bitcoin' or 'ethereum')
 * @param {string} tx.to - Recipient address
 * @param {string} tx.amount - Amount (as string to preserve BigInt)
 * @param {string} tx.fee - Fee (as string to preserve BigInt)
 * @param {number} tx.timestamp - Unix timestamp
 * @param {string} tx.status - Transaction status ('pending', 'confirmed', 'failed')
 */
async function storeTx(tx) {
  const { txHistoryDb } = await initializeStorage()

  // Key format: tx:<timestamp>:<hash> (sorted by time, unique by hash)
  const key = `tx:${tx.timestamp}:${tx.hash}`

  await txHistoryDb.put(key, {
    hash: tx.hash,
    chain: tx.chain,
    to: tx.to,
    amount: tx.amount,
    fee: tx.fee,
    timestamp: tx.timestamp,
    status: tx.status || 'pending'
  })
}

/**
 * Get all transactions, optionally filtered by chain
 * @param {string} chain - Optional chain filter ('bitcoin', 'ethereum', or null for all)
 * @returns {Array} Array of transaction objects
 */
async function getTxHistory(chain = null) {
  const { txHistoryDb } = await initializeStorage()

  const transactions = []

  // Read all transactions from Hyperbee
  for await (const entry of txHistoryDb.createReadStream()) {
    const tx = entry.value

    // Filter by chain if specified
    if (chain && tx.chain !== chain) {
      continue
    }

    transactions.push(tx)
  }

  // Sort by timestamp (newest first)
  transactions.sort((a, b) => b.timestamp - a.timestamp)

  return transactions
}

/**
 * Get a specific transaction by hash
 * @param {string} hash - Transaction hash
 * @returns {Object|null} Transaction object or null if not found
 */
async function getTx(hash) {
  const { txHistoryDb } = await initializeStorage()

  // Scan for transaction with matching hash
  for await (const entry of txHistoryDb.createReadStream()) {
    if (entry.value.hash === hash) {
      return entry.value
    }
  }

  return null
}

/**
 * Update transaction status
 * @param {string} hash - Transaction hash
 * @param {string} status - New status
 */
async function updateTxStatus(hash, status) {
  const { txHistoryDb } = await initializeStorage()

  // Find and update the transaction
  for await (const entry of txHistoryDb.createReadStream()) {
    if (entry.value.hash === hash) {
      const tx = entry.value
      tx.status = status
      await txHistoryDb.put(entry.key, tx)
      return
    }
  }
}

/**
 * Close storage (cleanup)
 */
async function closeStorage() {
  if (txHistoryDb) {
    await txHistoryDb.close()
    txHistoryDb = null
  }
  if (store) {
    await store.close()
    store = null
  }
}

module.exports = {
  initializeStorage,
  storeTx,
  getTxHistory,
  getTx,
  updateTxStatus,
  closeStorage
}