UNPKG

@arkade-os/sdk

Version:

Bitcoin wallet SDK with Taproot and Ark integration

186 lines (185 loc) 7.81 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IndexedDBVtxoRepository = void 0; class IndexedDBVtxoRepository { constructor() { this.db = null; } static delete() { return new Promise((resolve, reject) => { try { const request = indexedDB.deleteDatabase(IndexedDBVtxoRepository.DB_NAME); request.onblocked = () => { // If blocked, wait a bit and try again setTimeout(() => { const retryRequest = indexedDB.deleteDatabase(IndexedDBVtxoRepository.DB_NAME); retryRequest.onsuccess = () => resolve(); retryRequest.onerror = () => reject(retryRequest.error || new Error("Failed to delete database")); }, 100); }; request.onsuccess = () => { resolve(); }; request.onerror = () => { reject(request.error || new Error("Failed to delete database")); }; } catch (error) { reject(error instanceof Error ? error : new Error("Failed to delete database")); } }); } async close() { if (this.db) { this.db.close(); this.db = null; } } async open() { return new Promise((resolve, reject) => { const request = indexedDB.open(IndexedDBVtxoRepository.DB_NAME, IndexedDBVtxoRepository.DB_VERSION); request.onerror = () => { reject(request.error); }; request.onsuccess = () => { this.db = request.result; resolve(); }; request.onupgradeneeded = (event) => { const db = event.target.result; if (!db.objectStoreNames.contains(IndexedDBVtxoRepository.STORE_NAME)) { const store = db.createObjectStore(IndexedDBVtxoRepository.STORE_NAME, { keyPath: ["txid", "vout"], }); store.createIndex("state", "virtualStatus.state", { unique: false, }); store.createIndex("spentBy", "spentBy", { unique: false, }); } }; }); } async addOrUpdate(vtxos) { if (!this.db) { throw new Error("Database not opened"); } return new Promise((resolve, reject) => { const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readwrite"); const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME); const requests = vtxos.map((vtxo) => { return new Promise((resolveRequest, rejectRequest) => { const request = store.put(vtxo); request.onsuccess = () => resolveRequest(); request.onerror = () => rejectRequest(request.error); }); }); Promise.all(requests) .then(() => resolve()) .catch(reject); }); } async deleteAll() { if (!this.db) { throw new Error("Database not opened"); } return new Promise((resolve, reject) => { const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readwrite"); const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME); const request = store.clear(); request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); } async getSpendableVtxos() { if (!this.db) { throw new Error("Database not opened"); } return new Promise((resolve, reject) => { const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly"); const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME); const spentByIndex = store.index("spentBy"); // Get vtxos where spentBy is empty string const request = spentByIndex.getAll(IDBKeyRange.only("")); request.onsuccess = () => { resolve(request.result); }; request.onerror = () => reject(request.error); }); } async getSweptVtxos() { if (!this.db) { throw new Error("Database not opened"); } return new Promise((resolve, reject) => { const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly"); const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME); const stateIndex = store.index("state"); // Get vtxos where state is "swept" const request = stateIndex.getAll(IDBKeyRange.only("swept")); request.onsuccess = () => { resolve(request.result); }; request.onerror = () => reject(request.error); }); } async getSpentVtxos() { if (!this.db) { throw new Error("Database not opened"); } return new Promise((resolve, reject) => { const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly"); const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME); const spentByIndex = store.index("spentBy"); // Get vtxos where spentBy is not empty string const request = spentByIndex.getAll(IDBKeyRange.lowerBound("", true)); request.onsuccess = () => { resolve(request.result); }; request.onerror = () => reject(request.error); }); } async getAllVtxos() { if (!this.db) { throw new Error("Database not opened"); } return new Promise((resolve, reject) => { const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly"); const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME); const spentByIndex = store.index("spentBy"); // Get vtxos where spentBy is empty string const spendableRequest = spentByIndex.getAll(IDBKeyRange.only("")); // Get all vtxos where spentBy is populated const spentRequest = spentByIndex.getAll(IDBKeyRange.lowerBound("", true)); Promise.all([ new Promise((resolveSpendable, rejectSpendable) => { spendableRequest.onsuccess = () => { resolveSpendable(spendableRequest.result); }; spendableRequest.onerror = () => rejectSpendable(spendableRequest.error); }), new Promise((resolveSpent, rejectSpent) => { spentRequest.onsuccess = () => { resolveSpent(spentRequest.result); }; spentRequest.onerror = () => rejectSpent(spentRequest.error); }), ]) .then(([spendableVtxos, spentVtxos]) => { resolve({ spendable: spendableVtxos, spent: spentVtxos, }); }) .catch(reject); }); } } exports.IndexedDBVtxoRepository = IndexedDBVtxoRepository; IndexedDBVtxoRepository.DB_NAME = "wallet-db"; IndexedDBVtxoRepository.STORE_NAME = "vtxos"; IndexedDBVtxoRepository.DB_VERSION = 1;