UNPKG

bigblocks

Version:

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

217 lines (216 loc) 9.18 kB
import { PrivateKey } from "@bsv/sdk"; // useCreateListing Hook - Create marketplace listings import { useMutation } from "@tanstack/react-query"; import { TokenType, createOrdListings, createOrdTokenListings, } from "js-1sat-ord"; import { useBitcoinAuth } from "../../../hooks/useBitcoinAuth.js"; import { broadcastTransaction } from "../../../lib/broadcast"; import { getWalletProps, } from "../../wallet/types/extended-user.js"; export function useCreateListing(options = {}) { const { app: _app = "bigblocks", feeRate = 0.5, onSuccess, onError, } = options; const { user } = useBitcoinAuth(); // Create ordinal/NFT listings const createListingMutation = useMutation({ mutationFn: async (listings) => { if (!user) { throw { code: "UNAUTHORIZED", message: "User must be authenticated to create listings", }; } if (!listings.length) { throw { code: "INVALID_INPUT", message: "At least one listing is required", }; } try { // Get wallet properties with defaults const walletProps = getWalletProps(user); // Get user's private keys const paymentPk = walletProps.paymentKey ? PrivateKey.fromWif(walletProps.paymentKey) : undefined; const ordPk = walletProps.ordinalKey ? PrivateKey.fromWif(walletProps.ordinalKey) : undefined; if (!ordPk) { throw { code: "MISSING_KEYS", message: "Ordinal private key is required for listing", }; } // Get payment UTXOs for fees const paymentUtxos = walletProps.utxos; if (!paymentUtxos.length) { throw { code: "INSUFFICIENT_FUNDS", message: "No UTXOs available for transaction fees", }; } const config = { utxos: paymentUtxos, listings, paymentPk, ordPk, changeAddress: walletProps.fundingAddress, satsPerKb: Math.floor(feeRate * 1000), }; const { tx, spentOutpoints: _spentOutpoints } = await createOrdListings(config); // Broadcast transaction const broadcastResult = await broadcastTransaction(tx); if (!broadcastResult.success) { throw { code: "BROADCAST_FAILED", message: broadcastResult.error || "Failed to broadcast transaction", }; } const result = { txid: broadcastResult.txid ?? "", fee: tx.inputs.reduce((sum, input) => sum + (input.sourceTransaction?.outputs[input.sourceOutputIndex] ?.satoshis || 0), 0) - tx.outputs.reduce((sum, output) => sum + (output.satoshis || 0), 0), size: tx.toBinary().length, listings: listings.length, }; return result; } catch (error) { if (error.code) { throw error; } throw { code: "TRANSACTION_FAILED", message: "Failed to create listing transaction", details: error, }; } }, onSuccess: (data) => { onSuccess?.({ success: true, txid: data.txid }); }, onError: (error) => { console.error("Create listing failed:", error); onError?.(error); }, }); // Create token listings (BSV20/BSV21) const createTokenListingMutation = useMutation({ mutationFn: async ({ listings, params }) => { if (!user) { throw { code: "UNAUTHORIZED", message: "User must be authenticated to create token listings", }; } if (!listings.length) { throw { code: "INVALID_INPUT", message: "At least one token listing is required", }; } try { // Get wallet properties with defaults const walletProps = getWalletProps(user); // Get user's private keys const paymentPk = walletProps.paymentKey ? PrivateKey.fromWif(walletProps.paymentKey) : undefined; const ordPk = walletProps.ordinalKey ? PrivateKey.fromWif(walletProps.ordinalKey) : undefined; if (!ordPk) { throw { code: "MISSING_KEYS", message: "Ordinal private key is required for token listing", }; } // Get payment UTXOs and token UTXOs const paymentUtxos = walletProps.utxos; const tokenUtxos = walletProps.tokenUtxos.filter((utxo) => utxo.id === params.tokenID); if (!paymentUtxos.length) { throw { code: "INSUFFICIENT_FUNDS", message: "No UTXOs available for transaction fees", }; } if (!tokenUtxos.length) { throw { code: "INSUFFICIENT_TOKENS", message: "No tokens available for listing", }; } const config = { utxos: paymentUtxos, listings, paymentPk, ordPk, changeAddress: walletProps.fundingAddress, tokenChangeAddress: walletProps.ordinalAddress, satsPerKb: Math.floor(feeRate * 1000), protocol: params.protocol === "bsv20" ? TokenType.BSV20 : TokenType.BSV21, tokenID: params.tokenID, decimals: params.decimals, inputTokens: tokenUtxos, }; const { tx, spentOutpoints: _spentOutpoints2 } = await createOrdTokenListings(config); // Broadcast transaction const broadcastResult = await broadcastTransaction(tx); if (!broadcastResult.success) { throw { code: "BROADCAST_FAILED", message: broadcastResult.error || "Failed to broadcast transaction", }; } const result = { txid: broadcastResult.txid || "", fee: tx.inputs.reduce((sum, input) => sum + (input.sourceTransaction?.outputs[input.sourceOutputIndex] ?.satoshis || 0), 0) - tx.outputs.reduce((sum, output) => sum + (output.satoshis || 0), 0), size: tx.toBinary().length, listings: listings.length, }; return result; } catch (error) { if (error.code) { throw error; } throw { code: "TRANSACTION_FAILED", message: "Failed to create token listing transaction", details: error, }; } }, onSuccess: (data) => { onSuccess?.({ success: true, txid: data.txid }); }, onError: (error) => { console.error("Create token listing failed:", error); onError?.(error); }, }); return { // Mutations createListing: createListingMutation.mutate, createListingAsync: createListingMutation.mutateAsync, createTokenListing: (listings, params) => { createTokenListingMutation.mutate({ listings, params }); }, createTokenListingAsync: (listings, params) => { return createTokenListingMutation.mutateAsync({ listings, params }); }, // State isLoading: createListingMutation.isPending || createTokenListingMutation.isPending, isError: createListingMutation.isError || createTokenListingMutation.isError, isSuccess: createListingMutation.isSuccess || createTokenListingMutation.isSuccess, error: createListingMutation.error || createTokenListingMutation.error, // Reset reset: () => { createListingMutation.reset(); createTokenListingMutation.reset(); }, }; }