UNPKG

@arkade-os/sdk

Version:

Bitcoin wallet SDK with Taproot and Ark integration

172 lines (171 loc) 6.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WalletRepositoryImpl = void 0; const base_1 = require("@scure/base"); const btc_signer_1 = require("@scure/btc-signer"); const getVtxosStorageKey = (address) => `vtxos:${address}`; const getUtxosStorageKey = (address) => `utxos:${address}`; const getTransactionsStorageKey = (address) => `tx:${address}`; const walletStateStorageKey = "wallet:state"; // Utility functions for (de)serializing complex structures const toHex = (b) => (b ? base_1.hex.encode(b) : undefined); const fromHex = (h) => h ? base_1.hex.decode(h) : undefined; const serializeTapLeaf = ([cb, s]) => ({ cb: base_1.hex.encode(btc_signer_1.TaprootControlBlock.encode(cb)), s: base_1.hex.encode(s), }); const serializeVtxo = (v) => ({ ...v, tapTree: toHex(v.tapTree), forfeitTapLeafScript: serializeTapLeaf(v.forfeitTapLeafScript), intentTapLeafScript: serializeTapLeaf(v.intentTapLeafScript), extraWitness: v.extraWitness?.map(toHex), }); const serializeUtxo = (u) => ({ ...u, tapTree: toHex(u.tapTree), forfeitTapLeafScript: serializeTapLeaf(u.forfeitTapLeafScript), intentTapLeafScript: serializeTapLeaf(u.intentTapLeafScript), extraWitness: u.extraWitness?.map(toHex), }); const deserializeTapLeaf = (t) => { const cb = btc_signer_1.TaprootControlBlock.decode(fromHex(t.cb)); const s = fromHex(t.s); return [cb, s]; }; const deserializeVtxo = (o) => ({ ...o, createdAt: new Date(o.createdAt), tapTree: fromHex(o.tapTree), forfeitTapLeafScript: deserializeTapLeaf(o.forfeitTapLeafScript), intentTapLeafScript: deserializeTapLeaf(o.intentTapLeafScript), extraWitness: o.extraWitness?.map(fromHex), }); const deserializeUtxo = (o) => ({ ...o, tapTree: fromHex(o.tapTree), forfeitTapLeafScript: deserializeTapLeaf(o.forfeitTapLeafScript), intentTapLeafScript: deserializeTapLeaf(o.intentTapLeafScript), extraWitness: o.extraWitness?.map(fromHex), }); class WalletRepositoryImpl { constructor(storage) { this.storage = storage; } async getVtxos(address) { const stored = await this.storage.getItem(getVtxosStorageKey(address)); if (!stored) return []; try { const parsed = JSON.parse(stored); return parsed.map(deserializeVtxo); } catch (error) { console.error(`Failed to parse VTXOs for address ${address}:`, error); return []; } } async saveVtxos(address, vtxos) { const storedVtxos = await this.getVtxos(address); for (const vtxo of vtxos) { const existing = storedVtxos.findIndex((v) => v.txid === vtxo.txid && v.vout === vtxo.vout); if (existing !== -1) { storedVtxos[existing] = vtxo; } else { storedVtxos.push(vtxo); } } await this.storage.setItem(getVtxosStorageKey(address), JSON.stringify(storedVtxos.map(serializeVtxo))); } async removeVtxo(address, vtxoId) { const vtxos = await this.getVtxos(address); const [txid, vout] = vtxoId.split(":"); const filtered = vtxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10))); await this.storage.setItem(getVtxosStorageKey(address), JSON.stringify(filtered.map(serializeVtxo))); } async clearVtxos(address) { await this.storage.removeItem(getVtxosStorageKey(address)); } async getUtxos(address) { const stored = await this.storage.getItem(getUtxosStorageKey(address)); if (!stored) return []; try { const parsed = JSON.parse(stored); return parsed.map(deserializeUtxo); } catch (error) { console.error(`Failed to parse UTXOs for address ${address}:`, error); return []; } } async saveUtxos(address, utxos) { const storedUtxos = await this.getUtxos(address); utxos.forEach((utxo) => { const existing = storedUtxos.findIndex((u) => u.txid === utxo.txid && u.vout === utxo.vout); if (existing !== -1) { storedUtxos[existing] = utxo; } else { storedUtxos.push(utxo); } }); await this.storage.setItem(getUtxosStorageKey(address), JSON.stringify(storedUtxos.map(serializeUtxo))); } async removeUtxo(address, utxoId) { const utxos = await this.getUtxos(address); const [txid, vout] = utxoId.split(":"); const filtered = utxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10))); await this.storage.setItem(getUtxosStorageKey(address), JSON.stringify(filtered.map(serializeUtxo))); } async clearUtxos(address) { await this.storage.removeItem(getUtxosStorageKey(address)); } async getTransactionHistory(address) { const storageKey = getTransactionsStorageKey(address); const stored = await this.storage.getItem(storageKey); if (!stored) return []; try { return JSON.parse(stored); } catch (error) { console.error(`Failed to parse transactions for address ${address}:`, error); return []; } } async saveTransactions(address, txs) { const storedTransactions = await this.getTransactionHistory(address); for (const tx of txs) { const existing = storedTransactions.findIndex((t) => t.key === tx.key); if (existing !== -1) { storedTransactions[existing] = tx; } else { storedTransactions.push(tx); } } await this.storage.setItem(getTransactionsStorageKey(address), JSON.stringify(storedTransactions)); } async clearTransactions(address) { await this.storage.removeItem(getTransactionsStorageKey(address)); } async getWalletState() { const stored = await this.storage.getItem(walletStateStorageKey); if (!stored) return null; try { const state = JSON.parse(stored); return state; } catch (error) { console.error("Failed to parse wallet state:", error); return null; } } async saveWalletState(state) { await this.storage.setItem(walletStateStorageKey, JSON.stringify(state)); } } exports.WalletRepositoryImpl = WalletRepositoryImpl;