bigblocks
Version:
Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React
17 lines • 7.39 kB
JSON
{
"name": "inscriptions-hooks-useinscription",
"type": "registry:hook",
"dependencies": [
"@bsv/sdk"
],
"devDependencies": [],
"registryDependencies": [],
"files": [
{
"path": "components/inscriptions/hooks/useInscription.ts",
"type": "registry:hook",
"content": "/**\n * useInscription Hook\n *\n * Core hook for handling blockchain inscriptions\n * Provides shared functionality for all inscription types\n */\n\nimport { PrivateKey } from \"@bsv/sdk\";\nimport {\n\ttype CreateOrdinalsConfig,\n\ttype Destination,\n\ttype Inscription,\n\ttype LocalSigner,\n\ttype Payment,\n\ttype PreMAP,\n\ttype Utxo,\n\tcreateOrdinals,\n} from \"js-1sat-ord\";\nimport { useCallback, useState } from \"react\";\nimport { useBitcoinAuth } from \"../../../hooks/useBitcoinAuth.js\";\n\nexport interface InscriptionResult {\n\ttxid: string;\n\trawTx: string;\n\tfee: number;\n\tsize: number;\n\tnumInputs: number;\n\tnumOutputs: number;\n\tspentOutpoints: string[];\n\tpayChange?: Utxo;\n}\n\nexport interface UseInscriptionOptions {\n\tonSuccess?: (result: InscriptionResult) => void;\n\tonError?: (error: Error) => void;\n\tonProgress?: (status: string) => void;\n}\n\nexport interface InscribeParams {\n\tcontent: string | ArrayBuffer;\n\tcontentType: string;\n\tmetadata?: PreMAP;\n\titerations?: number;\n\tidentityKey?: string;\n\tadditionalPayments?: Payment[];\n\tutxos?: Utxo[];\n}\n\nexport function useInscription(options: UseInscriptionOptions = {}) {\n\tconst { walletExtension } = useBitcoinAuth();\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [result, setResult] = useState<InscriptionResult | null>(null);\n\tconst [feeEstimate, setFeeEstimate] = useState<number | null>(null);\n\n\tconst estimateFee = useCallback(\n\t\tasync (\n\t\t\tcontent: string | ArrayBuffer,\n\t\t\t_contentType: string,\n\t\t\titerations = 1,\n\t\t): Promise<number> => {\n\t\t\tif (!walletExtension) {\n\t\t\t\tthrow new Error(\"Wallet not connected\");\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Convert content to base64 if needed\n\t\t\t\tconst dataB64 =\n\t\t\t\t\ttypeof content === \"string\"\n\t\t\t\t\t\t? Buffer.from(content).toString(\"base64\")\n\t\t\t\t\t\t: Buffer.from(content).toString(\"base64\");\n\n\t\t\t\t// Calculate inscription size\n\t\t\t\tconst inscriptionSize = Buffer.from(dataB64, \"base64\").length;\n\n\t\t\t\t// Estimate transaction size (rough calculation)\n\t\t\t\t// Base transaction overhead + inscription data + script overhead\n\t\t\t\tconst baseSize = 250; // Base transaction size\n\t\t\t\tconst scriptOverhead = 100; // OP_RETURN and other script overhead\n\t\t\t\tconst totalSize =\n\t\t\t\t\tbaseSize + (inscriptionSize + scriptOverhead) * iterations;\n\n\t\t\t\t// Use 1 sat/byte as default fee rate\n\t\t\t\tconst feeRate = 1;\n\t\t\t\tconst estimatedFee = Math.ceil(totalSize * feeRate);\n\n\t\t\t\tsetFeeEstimate(estimatedFee);\n\t\t\t\treturn estimatedFee;\n\t\t\t} catch (err) {\n\t\t\t\tconst error =\n\t\t\t\t\terr instanceof Error ? err : new Error(\"Failed to estimate fee\");\n\t\t\t\tsetError(error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\t[walletExtension],\n\t);\n\n\tconst inscribe = useCallback(\n\t\tasync ({\n\t\t\tcontent,\n\t\t\tcontentType,\n\t\t\tmetadata,\n\t\t\titerations = 1,\n\t\t\tidentityKey,\n\t\t\tadditionalPayments = [],\n\t\t\tutxos,\n\t\t}: InscribeParams): Promise<InscriptionResult> => {\n\t\t\tif (!walletExtension) {\n\t\t\t\tthrow new Error(\"Wallet not connected\");\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t!walletExtension.paymentKey ||\n\t\t\t\t!walletExtension.ordinalKey ||\n\t\t\t\t!walletExtension.ordinalAddress ||\n\t\t\t\t!walletExtension.fundingAddress\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Wallet not properly initialized\");\n\t\t\t}\n\n\t\t\tsetLoading(true);\n\t\t\tsetError(null);\n\t\t\toptions.onProgress?.(\"Preparing inscription...\");\n\n\t\t\ttry {\n\t\t\t\t// Get UTXOs if not provided\n\t\t\t\tconst fundingUtxos =\n\t\t\t\t\tutxos ||\n\t\t\t\t\twalletExtension.utxos?.map((u) => ({\n\t\t\t\t\t\t...u,\n\t\t\t\t\t\tscript: u.script || \"\",\n\t\t\t\t\t})) ||\n\t\t\t\t\t[];\n\t\t\t\tif (fundingUtxos.length === 0) {\n\t\t\t\t\tthrow new Error(\"No UTXOs available for inscription\");\n\t\t\t\t}\n\n\t\t\t\t// Convert content to base64\n\t\t\t\tconst dataB64 =\n\t\t\t\t\ttypeof content === \"string\"\n\t\t\t\t\t\t? Buffer.from(content).toString(\"base64\")\n\t\t\t\t\t\t: Buffer.from(content).toString(\"base64\");\n\n\t\t\t\t// Create inscriptions array for iterations\n\t\t\t\tconst inscriptions: Inscription[] = Array(iterations)\n\t\t\t\t\t.fill(null)\n\t\t\t\t\t.map(() => ({\n\t\t\t\t\t\tdataB64,\n\t\t\t\t\t\tcontentType,\n\t\t\t\t\t}));\n\n\t\t\t\t// Create destinations\n\t\t\t\tconst destinations: Destination[] = inscriptions.map((inscription) => ({\n\t\t\t\t\taddress: walletExtension.ordinalAddress || \"\",\n\t\t\t\t\tinscription,\n\t\t\t\t}));\n\n\t\t\t\t// Setup signer if identity key provided\n\t\t\t\tlet signer: LocalSigner | undefined;\n\t\t\t\tif (identityKey) {\n\t\t\t\t\tconst idKey = PrivateKey.fromWif(identityKey);\n\t\t\t\t\tsigner = { idKey } as LocalSigner;\n\t\t\t\t}\n\n\t\t\t\t// Create ordinals configuration\n\t\t\t\tconst config: CreateOrdinalsConfig = {\n\t\t\t\t\tutxos: fundingUtxos,\n\t\t\t\t\tdestinations,\n\t\t\t\t\tpaymentPk: PrivateKey.fromWif(walletExtension.paymentKey),\n\t\t\t\t\tmetaData: metadata,\n\t\t\t\t\tadditionalPayments,\n\t\t\t\t\tsigner,\n\t\t\t\t\tchangeAddress: walletExtension.fundingAddress,\n\t\t\t\t};\n\n\t\t\t\toptions.onProgress?.(\"Creating inscription transaction...\");\n\n\t\t\t\t// Create the inscription transaction\n\t\t\t\tconst { tx, spentOutpoints, payChange } = await createOrdinals(config);\n\n\t\t\t\t// Calculate actual fee\n\t\t\t\tconst satsIn = fundingUtxos.reduce(\n\t\t\t\t\t(acc, utxo) => acc + utxo.satoshis,\n\t\t\t\t\t0,\n\t\t\t\t);\n\t\t\t\tconst satsOut = tx.outputs.reduce(\n\t\t\t\t\t(acc, output) => acc + (output.satoshis || 0),\n\t\t\t\t\t0,\n\t\t\t\t);\n\t\t\t\tconst fee = satsIn - satsOut;\n\n\t\t\t\tconst inscriptionResult: InscriptionResult = {\n\t\t\t\t\ttxid: tx.id(\"hex\"),\n\t\t\t\t\trawTx: tx.toHex(),\n\t\t\t\t\tfee,\n\t\t\t\t\tsize: tx.toBinary().length,\n\t\t\t\t\tnumInputs: tx.inputs.length,\n\t\t\t\t\tnumOutputs: tx.outputs.length,\n\t\t\t\t\tspentOutpoints,\n\t\t\t\t\tpayChange,\n\t\t\t\t};\n\n\t\t\t\toptions.onProgress?.(\"Broadcasting transaction...\");\n\n\t\t\t\t// Broadcast transaction (this would be handled by the app's broadcast service)\n\t\t\t\t// For now, we just return the result for the app to broadcast\n\n\t\t\t\tsetResult(inscriptionResult);\n\t\t\t\toptions.onSuccess?.(inscriptionResult);\n\n\t\t\t\treturn inscriptionResult;\n\t\t\t} catch (err) {\n\t\t\t\tconst error =\n\t\t\t\t\terr instanceof Error ? err : new Error(\"Inscription failed\");\n\t\t\t\tsetError(error);\n\t\t\t\toptions.onError?.(error);\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tsetLoading(false);\n\t\t\t}\n\t\t},\n\t\t[walletExtension, options],\n\t);\n\n\tconst reset = useCallback(() => {\n\t\tsetError(null);\n\t\tsetResult(null);\n\t\tsetFeeEstimate(null);\n\t}, []);\n\n\treturn {\n\t\tinscribe,\n\t\testimateFee,\n\t\tloading,\n\t\terror,\n\t\tresult,\n\t\tfeeEstimate,\n\t\treset,\n\t};\n}\n",
"target": "<%- config.aliases.hooks %>/useinscription.ts"
}
]
}