bigblocks
Version:
Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React
150 lines (149 loc) • 6.02 kB
JavaScript
/**
* 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,
};
}