UNPKG

@arklabs/wallet-sdk

Version:

Bitcoin wallet SDK with Taproot and Ark integration

277 lines (276 loc) 9.98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ServiceWorkerWallet = void 0; const response_1 = require("./response"); // ServiceWorkerWallet is a wallet that uses a service worker as "backend" to handle the wallet logic class ServiceWorkerWallet { static async create(svcWorkerPath) { try { const wallet = new ServiceWorkerWallet(); await wallet.setupServiceWorker(svcWorkerPath); return wallet; } catch (error) { throw new Error(`Failed to initialize service worker wallet: ${error}`); } } async init(config, failIfInitialized = false) { // Check if wallet is already initialized const statusMessage = { type: "GET_STATUS", }; const response = await this.sendMessage(statusMessage); if (response_1.Response.isWalletStatus(response) && response.status.walletInitialized) { if (failIfInitialized) { throw new Error("Wallet already initialized"); } return; } // If not initialized, proceed with initialization const message = { type: "INIT_WALLET", privateKey: config.privateKey, network: config.network, arkServerUrl: config.arkServerUrl || "", arkServerPublicKey: config.arkServerPublicKey, }; await this.sendMessage(message); } async clear() { const message = { type: "CLEAR", }; await this.sendMessage(message); } // register the service worker async setupServiceWorker(path) { // check if service workers are supported if (!("serviceWorker" in navigator)) { throw new Error("Service workers are not supported in this browser"); } try { // check for existing registration const existingRegistration = await navigator.serviceWorker.getRegistration(path); let registration; if (existingRegistration) { registration = existingRegistration; // Force unregister and re-register to ensure we get the latest version await existingRegistration.unregister(); } registration = await navigator.serviceWorker.register(path); // Handle updates registration.addEventListener("updatefound", () => { console.info("@arklabs/wallet-sdk: Service worker auto-update..."); const newWorker = registration.installing; if (!newWorker) return; newWorker.addEventListener("statechange", () => { if (newWorker.state === "installed" && navigator.serviceWorker.controller) { console.info("@arklabs/wallet-sdk: Service worker updated, reloading..."); window.location.reload(); } }); }); // Check for updates await registration.update(); const sw = registration.active || registration.waiting || registration.installing; if (!sw) { throw new Error("Failed to get service worker instance"); } this.serviceWorker = sw; // wait for the service worker to be ready if (this.serviceWorker?.state !== "activated") { await new Promise((resolve) => { if (!this.serviceWorker) return resolve(); this.serviceWorker.addEventListener("statechange", () => { if (this.serviceWorker?.state === "activated") { resolve(); } }); }); } } catch (error) { throw new Error(`Failed to setup service worker: ${error}`); } } // send a message and wait for a response async sendMessage(message) { if (!this.serviceWorker) { throw new Error("Service worker not initialized"); } return new Promise((resolve, reject) => { const messageHandler = (event) => { const response = event.data; navigator.serviceWorker.removeEventListener("message", messageHandler); if (!response.success) { reject(new Error(response.message)); } else { resolve(response); } }; navigator.serviceWorker.addEventListener("message", messageHandler); if (this.serviceWorker) { this.serviceWorker.postMessage(message); } else { reject(new Error("Service worker not initialized")); } }); } async getAddress() { const message = { type: "GET_ADDRESS", }; try { const response = await this.sendMessage(message); if (response_1.Response.isAddress(response)) { return response.address; } throw new Error("Unexpected response type"); } catch (error) { throw new Error(`Failed to get address: ${error}`); } } async getBalance() { const message = { type: "GET_BALANCE", }; try { const response = await this.sendMessage(message); if (response_1.Response.isBalance(response)) { return response.balance; } throw new Error("Unexpected response type"); } catch (error) { throw new Error(`Failed to get balance: ${error}`); } } async getCoins() { const message = { type: "GET_COINS", }; try { const response = await this.sendMessage(message); if (response_1.Response.isCoins(response)) { return response.coins; } throw new Error("Unexpected response type"); } catch (error) { throw new Error(`Failed to get coins: ${error}`); } } async getVtxos() { const message = { type: "GET_VTXOS", }; try { const response = await this.sendMessage(message); if (response_1.Response.isVtxos(response)) { return response.vtxos; } throw new Error("Unexpected response type"); } catch (error) { throw new Error(`Failed to get vtxos: ${error}`); } } async getBoardingUtxos() { const message = { type: "GET_BOARDING_UTXOS", }; try { const response = await this.sendMessage(message); if (response_1.Response.isBoardingUtxos(response)) { return response.boardingUtxos; } throw new Error("Unexpected response type"); } catch (error) { throw new Error(`Failed to get boarding UTXOs: ${error}`); } } async sendBitcoin(params, zeroFee) { const message = { type: "SEND_BITCOIN", params, zeroFee, }; try { const response = await this.sendMessage(message); if (response_1.Response.isSendBitcoinSuccess(response)) { return response.txid; } throw new Error("Unexpected response type"); } catch (error) { throw new Error(`Failed to send bitcoin: ${error}`); } } async settle(params, callback) { const message = { type: "SETTLE", params, }; try { return new Promise((resolve, reject) => { const messageHandler = (event) => { const response = event.data; if (!response.success) { navigator.serviceWorker.removeEventListener("message", messageHandler); reject(new Error(response.message)); return; } switch (response.type) { case "SETTLE_EVENT": if (callback) { callback(response.event); } break; case "SETTLE_SUCCESS": navigator.serviceWorker.removeEventListener("message", messageHandler); resolve(response.txid); break; default: break; } }; navigator.serviceWorker.addEventListener("message", messageHandler); if (this.serviceWorker) { this.serviceWorker.postMessage(message); } else { reject(new Error("Service worker not initialized")); } }); } catch (error) { throw new Error(`Settlement failed: ${error}`); } } async getTransactionHistory() { const message = { type: "GET_TRANSACTION_HISTORY", }; try { const response = await this.sendMessage(message); if (response_1.Response.isTransactionHistory(response)) { return response.transactions; } throw new Error("Unexpected response type"); } catch (error) { throw new Error(`Failed to get transaction history: ${error}`); } } } exports.ServiceWorkerWallet = ServiceWorkerWallet;