UNPKG

bigblocks

Version:

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

17 lines 7.64 kB
{ "name": "social-utils-broadcast", "type": "registry:component", "dependencies": [ "@bsv/sdk" ], "devDependencies": [], "registryDependencies": [], "files": [ { "path": "components/social/utils/broadcast.ts", "type": "registry:component", "content": "// Broadcast Utilities - Send transactions to Bitcoin network\nimport { P2PKH, Transaction } from \"@bsv/sdk\";\nimport type {\n\tBroadcastConfig,\n\tSignedProtocolData,\n\tTransactionBuilderConfig,\n\tUTXO,\n} from \"../types/protocol.js\";\nimport type { BroadcastResult } from \"../types/social.js\";\nimport { buildOpReturnScript } from \"./protocol.js\";\n\n// Default broadcast configuration\nconst DEFAULT_BROADCAST_CONFIG: BroadcastConfig = {\n\tbroadcaster: \"whatsonchain\",\n\ttimeout: 30000,\n\tretries: 3,\n};\n\n// Build complete transaction with funding and change\nexport async function buildCompleteTransaction(\n\tsignedData: SignedProtocolData,\n\tconfig: TransactionBuilderConfig,\n): Promise<Transaction> {\n\tconst { utxos, changeAddress, paymentKey, feePerByte = 0.5 } = config;\n\n\t// Create OP_RETURN script from signed data\n\tconst opReturnScript = buildOpReturnScript(signedData);\n\n\t// Create transaction\n\tconst tx = new Transaction();\n\n\t// Add OP_RETURN output (0 satoshis)\n\ttx.addOutput({\n\t\tlockingScript: opReturnScript,\n\t\tsatoshis: 0,\n\t});\n\n\t// Calculate total input value\n\tlet totalInputValue = 0;\n\tfor (const utxo of utxos) {\n\t\ttx.addInput({\n\t\t\tsourceTransaction: utxo.tx,\n\t\t\tsourceOutputIndex: utxo.vout,\n\t\t\tunlockingScriptTemplate: new P2PKH().unlock(paymentKey),\n\t\t});\n\t\ttotalInputValue += utxo.satoshis;\n\t}\n\n\t// Calculate estimated fee\n\tconst estimatedSize = tx.toHex().length / 2 + 150; // Rough estimation\n\tconst fee = Math.ceil(estimatedSize * feePerByte);\n\n\t// Add change output if necessary\n\tconst changeAmount = totalInputValue - fee;\n\tif (changeAmount > 546) {\n\t\t// Dust limit\n\t\ttx.addOutput({\n\t\t\tlockingScript: new P2PKH().lock(changeAddress),\n\t\t\tsatoshis: changeAmount,\n\t\t});\n\t}\n\n\treturn tx;\n}\n\n// Broadcast transaction to Bitcoin network\nexport async function broadcastSocialTransaction(\n\tsignedData: SignedProtocolData,\n\tconfig?: BroadcastConfig,\n): Promise<BroadcastResult> {\n\tconst finalConfig = { ...DEFAULT_BROADCAST_CONFIG, ...(config || {}) };\n\n\ttry {\n\t\t// For now, we'll create a minimal transaction structure\n\t\t// In a real implementation, you would:\n\t\t// 1. Get UTXOs for the user\n\t\t// 2. Build a complete transaction with inputs and change\n\t\t// 3. Sign all inputs\n\t\t// 4. Broadcast the raw transaction\n\n\t\t// This is a simplified version that assumes external transaction building\n\t\tconst mockTxId = generateMockTxId();\n\n\t\t// In practice, you would use a real broadcaster:\n\t\tconst result = await broadcastWithRetry(signedData, finalConfig);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\ttxid: result.txid || mockTxId,\n\t\t\trawTx: result.rawTx,\n\t\t\tfee: result.fee || 150, // Default fee estimation\n\t\t};\n\t} catch (error) {\n\t\tconsole.error(\"Broadcast failed:\", error);\n\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: error instanceof Error ? error.message : \"Unknown broadcast error\",\n\t\t};\n\t}\n}\n\n// Retry logic for broadcasting\nasync function broadcastWithRetry(\n\tsignedData: SignedProtocolData,\n\tconfig: BroadcastConfig,\n\tattempt = 1,\n): Promise<{ txid: string; rawTx?: string; fee?: number }> {\n\ttry {\n\t\t// Mock implementation - replace with real broadcaster\n\t\tif (attempt <= (config.retries || 1)) {\n\t\t\t// Simulate network call\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 100));\n\n\t\t\t// Mock success\n\t\t\treturn {\n\t\t\t\ttxid: generateMockTxId(),\n\t\t\t\trawTx: \"mock_raw_transaction_hex\",\n\t\t\t\tfee: 150,\n\t\t\t};\n\t\t}\n\n\t\tthrow new Error(\"Max retries exceeded\");\n\t} catch (error) {\n\t\tif (attempt < (config.retries || 1)) {\n\t\t\tconsole.warn(`Broadcast attempt ${attempt} failed, retrying...`);\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 1000 * attempt));\n\t\t\treturn broadcastWithRetry(signedData, config, attempt + 1);\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\n// Generate mock transaction ID for testing\nfunction generateMockTxId(): string {\n\tconst chars = \"0123456789abcdef\";\n\tlet result = \"\";\n\tfor (let i = 0; i < 64; i++) {\n\t\tresult += chars.charAt(Math.floor(Math.random() * chars.length));\n\t}\n\treturn result;\n}\n\n// Real broadcaster implementations would go here:\n\n// WhatsOnChain broadcaster (deprecated - use BlockchainService instead)\nexport async function broadcastWithWhatsOnChain(\n\trawTx: string,\n): Promise<string> {\n\tconsole.warn(\n\t\t\"broadcastWithWhatsOnChain is deprecated. Use BlockchainService instead.\",\n\t);\n\tconst response = await fetch(\n\t\t\"https://api.whatsonchain.com/v1/bsv/main/tx/raw\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({ txhex: rawTx }),\n\t\t},\n\t);\n\n\tif (!response.ok) {\n\t\tthrow new Error(`WhatsOnChain broadcast failed: ${response.statusText}`);\n\t}\n\n\treturn await response.text(); // Returns txid\n}\n\n// GorillaPool broadcaster (deprecated - use BlockchainService instead)\nexport async function broadcastWithGorillaPool(rawTx: string): Promise<string> {\n\tconsole.warn(\n\t\t\"broadcastWithGorillaPool is deprecated. Use BlockchainService instead.\",\n\t);\n\tconst response = await fetch(\"https://arc.gorillapool.io/v1/tx\", {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t\tbody: JSON.stringify({ rawtx: rawTx }),\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`GorillaPool broadcast failed: ${response.statusText}`);\n\t}\n\n\tconst result = await response.json();\n\treturn result.txid;\n}\n\n// TAAL broadcaster (deprecated - use BlockchainService instead)\nexport async function broadcastWithTaal(\n\trawTx: string,\n\tapiKey?: string,\n): Promise<string> {\n\tconsole.warn(\n\t\t\"broadcastWithTaal is deprecated. Use BlockchainService instead.\",\n\t);\n\tconst response = await fetch(\"https://api.taal.com/arc/v1/tx\", {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\tAuthorization: `Bearer ${apiKey || \"YOUR_TAAL_API_KEY\"}`, // Would need to be configured\n\t\t},\n\t\tbody: JSON.stringify({ rawtx: rawTx }),\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`TAAL broadcast failed: ${response.statusText}`);\n\t}\n\n\tconst result = await response.json();\n\treturn result.txid;\n}\n\n// Utility to get UTXOs for a given address (deprecated - use BlockchainService instead)\nexport async function getUtxos(address: string): Promise<UTXO[]> {\n\tconsole.warn(\n\t\t\"getUtxos is deprecated. Use BlockchainService.getUTXOs() instead.\",\n\t);\n\ttry {\n\t\tconst response = await fetch(\n\t\t\t`https://api.whatsonchain.com/v1/bsv/main/address/${address}/unspent`,\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\"Failed to fetch UTXOs\");\n\t\t}\n\n\t\tconst utxos = await response.json();\n\n\t\treturn utxos.map(\n\t\t\t(utxo: { tx_hash: string; tx_pos: number; value: number }) => ({\n\t\t\t\ttxid: utxo.tx_hash,\n\t\t\t\tvout: utxo.tx_pos,\n\t\t\t\tsatoshis: utxo.value,\n\t\t\t\tscript: \"\", // Would need to fetch from transaction\n\t\t\t}),\n\t\t);\n\t} catch (error) {\n\t\tconsole.error(\"Failed to get UTXOs:\", error);\n\t\treturn [];\n\t}\n}\n", "target": "<%- config.aliases.components %>/broadcast.tsx" } ] }