UNPKG

bigblocks

Version:

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

150 lines (149 loc) 6.02 kB
/** * useInscription Hook * * Core hook for handling blockchain inscriptions * Provides shared functionality for all inscription types */ import { PrivateKey } from "@bsv/sdk"; import { createOrdinals, } from "js-1sat-ord"; import { useCallback, useState } from "react"; import { useBitcoinAuth } from "../../../hooks/useBitcoinAuth.js"; export function useInscription(options = {}) { const { walletExtension } = useBitcoinAuth(); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [result, setResult] = useState(null); const [feeEstimate, setFeeEstimate] = useState(null); const estimateFee = useCallback(async (content, _contentType, iterations = 1) => { if (!walletExtension) { throw new Error("Wallet not connected"); } try { // Convert content to base64 if needed const dataB64 = typeof content === "string" ? Buffer.from(content).toString("base64") : Buffer.from(content).toString("base64"); // Calculate inscription size const inscriptionSize = Buffer.from(dataB64, "base64").length; // Estimate transaction size (rough calculation) // Base transaction overhead + inscription data + script overhead const baseSize = 250; // Base transaction size const scriptOverhead = 100; // OP_RETURN and other script overhead const totalSize = baseSize + (inscriptionSize + scriptOverhead) * iterations; // Use 1 sat/byte as default fee rate const feeRate = 1; const estimatedFee = Math.ceil(totalSize * feeRate); setFeeEstimate(estimatedFee); return estimatedFee; } catch (err) { const error = err instanceof Error ? err : new Error("Failed to estimate fee"); setError(error); throw error; } }, [walletExtension]); const inscribe = useCallback(async ({ content, contentType, metadata, iterations = 1, identityKey, additionalPayments = [], utxos, }) => { if (!walletExtension) { throw new Error("Wallet not connected"); } if (!walletExtension.paymentKey || !walletExtension.ordinalKey || !walletExtension.ordinalAddress || !walletExtension.fundingAddress) { throw new Error("Wallet not properly initialized"); } setLoading(true); setError(null); options.onProgress?.("Preparing inscription..."); try { // Get UTXOs if not provided const fundingUtxos = utxos || walletExtension.utxos?.map((u) => ({ ...u, script: u.script || "", })) || []; if (fundingUtxos.length === 0) { throw new Error("No UTXOs available for inscription"); } // Convert content to base64 const dataB64 = typeof content === "string" ? Buffer.from(content).toString("base64") : Buffer.from(content).toString("base64"); // Create inscriptions array for iterations const inscriptions = Array(iterations) .fill(null) .map(() => ({ dataB64, contentType, })); // Create destinations const destinations = inscriptions.map((inscription) => ({ address: walletExtension.ordinalAddress || "", inscription, })); // Setup signer if identity key provided let signer; if (identityKey) { const idKey = PrivateKey.fromWif(identityKey); signer = { idKey }; } // Create ordinals configuration const config = { utxos: fundingUtxos, destinations, paymentPk: PrivateKey.fromWif(walletExtension.paymentKey), metaData: metadata, additionalPayments, signer, changeAddress: walletExtension.fundingAddress, }; options.onProgress?.("Creating inscription transaction..."); // Create the inscription transaction const { tx, spentOutpoints, payChange } = await createOrdinals(config); // Calculate actual fee const satsIn = fundingUtxos.reduce((acc, utxo) => acc + utxo.satoshis, 0); const satsOut = tx.outputs.reduce((acc, output) => acc + (output.satoshis || 0), 0); const fee = satsIn - satsOut; const inscriptionResult = { txid: tx.id("hex"), rawTx: tx.toHex(), fee, size: tx.toBinary().length, numInputs: tx.inputs.length, numOutputs: tx.outputs.length, spentOutpoints, payChange, }; options.onProgress?.("Broadcasting transaction..."); // Broadcast transaction (this would be handled by the app's broadcast service) // For now, we just return the result for the app to broadcast setResult(inscriptionResult); options.onSuccess?.(inscriptionResult); return inscriptionResult; } catch (err) { const error = err instanceof Error ? err : new Error("Inscription failed"); setError(error); options.onError?.(error); throw error; } finally { setLoading(false); } }, [walletExtension, options]); const reset = useCallback(() => { setError(null); setResult(null); setFeeEstimate(null); }, []); return { inscribe, estimateFee, loading, error, result, feeEstimate, reset, }; }