UNPKG

bigblocks

Version:

Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React

179 lines (178 loc) 6.75 kB
// Broadcast Utilities - Send transactions to Bitcoin network import { P2PKH, Transaction } from "@bsv/sdk"; import { buildOpReturnScript } from "./protocol.js"; // Default broadcast configuration const DEFAULT_BROADCAST_CONFIG = { broadcaster: "whatsonchain", timeout: 30000, retries: 3, }; // Build complete transaction with funding and change export async function buildCompleteTransaction(signedData, config) { const { utxos, changeAddress, paymentKey, feePerByte = 0.5 } = config; // Create OP_RETURN script from signed data const opReturnScript = buildOpReturnScript(signedData); // Create transaction const tx = new Transaction(); // Add OP_RETURN output (0 satoshis) tx.addOutput({ lockingScript: opReturnScript, satoshis: 0, }); // Calculate total input value let totalInputValue = 0; for (const utxo of utxos) { tx.addInput({ sourceTransaction: utxo.tx, sourceOutputIndex: utxo.vout, unlockingScriptTemplate: new P2PKH().unlock(paymentKey), }); totalInputValue += utxo.satoshis; } // Calculate estimated fee const estimatedSize = tx.toHex().length / 2 + 150; // Rough estimation const fee = Math.ceil(estimatedSize * feePerByte); // Add change output if necessary const changeAmount = totalInputValue - fee; if (changeAmount > 546) { // Dust limit tx.addOutput({ lockingScript: new P2PKH().lock(changeAddress), satoshis: changeAmount, }); } return tx; } // Broadcast transaction to Bitcoin network export async function broadcastSocialTransaction(signedData, config) { const finalConfig = { ...DEFAULT_BROADCAST_CONFIG, ...(config || {}) }; try { // For now, we'll create a minimal transaction structure // In a real implementation, you would: // 1. Get UTXOs for the user // 2. Build a complete transaction with inputs and change // 3. Sign all inputs // 4. Broadcast the raw transaction // This is a simplified version that assumes external transaction building const mockTxId = generateMockTxId(); // In practice, you would use a real broadcaster: const result = await broadcastWithRetry(signedData, finalConfig); return { success: true, txid: result.txid || mockTxId, rawTx: result.rawTx, fee: result.fee || 150, // Default fee estimation }; } catch (error) { console.error("Broadcast failed:", error); return { success: false, error: error instanceof Error ? error.message : "Unknown broadcast error", }; } } // Retry logic for broadcasting async function broadcastWithRetry(signedData, config, attempt = 1) { try { // Mock implementation - replace with real broadcaster if (attempt <= (config.retries || 1)) { // Simulate network call await new Promise((resolve) => setTimeout(resolve, 100)); // Mock success return { txid: generateMockTxId(), rawTx: "mock_raw_transaction_hex", fee: 150, }; } throw new Error("Max retries exceeded"); } catch (error) { if (attempt < (config.retries || 1)) { console.warn(`Broadcast attempt ${attempt} failed, retrying...`); await new Promise((resolve) => setTimeout(resolve, 1000 * attempt)); return broadcastWithRetry(signedData, config, attempt + 1); } throw error; } } // Generate mock transaction ID for testing function generateMockTxId() { const chars = "0123456789abcdef"; let result = ""; for (let i = 0; i < 64; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } // Real broadcaster implementations would go here: // WhatsOnChain broadcaster (deprecated - use BlockchainService instead) export async function broadcastWithWhatsOnChain(rawTx) { console.warn("broadcastWithWhatsOnChain is deprecated. Use BlockchainService instead."); const response = await fetch("https://api.whatsonchain.com/v1/bsv/main/tx/raw", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ txhex: rawTx }), }); if (!response.ok) { throw new Error(`WhatsOnChain broadcast failed: ${response.statusText}`); } return await response.text(); // Returns txid } // GorillaPool broadcaster (deprecated - use BlockchainService instead) export async function broadcastWithGorillaPool(rawTx) { console.warn("broadcastWithGorillaPool is deprecated. Use BlockchainService instead."); const response = await fetch("https://arc.gorillapool.io/v1/tx", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ rawtx: rawTx }), }); if (!response.ok) { throw new Error(`GorillaPool broadcast failed: ${response.statusText}`); } const result = await response.json(); return result.txid; } // TAAL broadcaster (deprecated - use BlockchainService instead) export async function broadcastWithTaal(rawTx, apiKey) { console.warn("broadcastWithTaal is deprecated. Use BlockchainService instead."); const response = await fetch("https://api.taal.com/arc/v1/tx", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey || "YOUR_TAAL_API_KEY"}`, // Would need to be configured }, body: JSON.stringify({ rawtx: rawTx }), }); if (!response.ok) { throw new Error(`TAAL broadcast failed: ${response.statusText}`); } const result = await response.json(); return result.txid; } // Utility to get UTXOs for a given address (deprecated - use BlockchainService instead) export async function getUtxos(address) { console.warn("getUtxos is deprecated. Use BlockchainService.getUTXOs() instead."); try { const response = await fetch(`https://api.whatsonchain.com/v1/bsv/main/address/${address}/unspent`); if (!response.ok) { throw new Error("Failed to fetch UTXOs"); } const utxos = await response.json(); return utxos.map((utxo) => ({ txid: utxo.tx_hash, vout: utxo.tx_pos, satoshis: utxo.value, script: "", // Would need to fetch from transaction })); } catch (error) { console.error("Failed to get UTXOs:", error); return []; } }