UNPKG

bigblocks

Version:

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

173 lines (172 loc) 7.46 kB
import { PrivateKey, } from "@bsv/sdk"; // useCancelListing Hook - Cancel marketplace listings import { useMutation } from "@tanstack/react-query"; import { cancelOrdListings, cancelOrdTokenListings, } from "js-1sat-ord"; import { useBitcoinAuth } from "../../../hooks/useBitcoinAuth.js"; import { broadcastTransaction } from "../../../lib/broadcast"; import { getWalletProps, } from "../../wallet/types/extended-user.js"; import { AssetType, } from "../types/market.js"; export function useCancelListing(options = {}) { const { app: _app = "bigblocks", feeRate = 0.5, onSuccess, onError, } = options; const { user } = useBitcoinAuth(); const cancelMutation = useMutation({ mutationFn: async (listing) => { if (!user) { throw { code: "UNAUTHORIZED", message: "User must be authenticated to cancel listings", }; } 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 cancelling listings", }; } // Get payment UTXOs const paymentUtxos = walletProps.utxos; if (!paymentUtxos.length) { throw { code: "INSUFFICIENT_FUNDS", message: "No UTXOs available for transaction fees", }; } // Create listing UTXO from the listing data const listingUtxo = { txid: listing.txid, vout: listing.vout, satoshis: listing.assetType === AssetType.Ordinals ? listing.priceSats : 1, // Token UTXOs always contain 1 satoshi script: listing.ordAddress, }; let tx; let _spentOutpoints; if (listing.assetType === AssetType.Ordinals) { // Cancel ordinal/NFT listing const config = { utxos: paymentUtxos, paymentPk, ordPk, listingUtxos: [listingUtxo], changeAddress: walletProps.fundingAddress, satsPerKb: Math.floor(feeRate * 1000), additionalPayments: [], }; const result = await cancelOrdListings(config); tx = result.tx; _spentOutpoints = result.spentOutpoints; } else { // Cancel token listing (BSV20/BSV21) if (!listing.tokenId || !listing.tokenAmount) { throw { code: "INVALID_INPUT", message: "Token ID and amount are required for token listing cancellation", }; } // Convert AssetType to TokenType let protocol; if (listing.assetType === AssetType.BSV20) { protocol = "BSV20"; } else if (listing.assetType === AssetType.BSV21) { protocol = "BSV21"; } else { throw { code: "INVALID_INPUT", message: "Invalid token type for cancellation", }; } const tokenListingUtxo = { ...listingUtxo, satoshis: 1, // Token UTXOs always contain exactly 1 satoshi amt: listing.tokenAmount, id: listing.tokenId, }; const config = { protocol, tokenID: listing.tokenId, utxos: paymentUtxos, paymentPk, ordPk, listingUtxos: [tokenListingUtxo], ordAddress: walletProps.ordinalAddress, changeAddress: walletProps.fundingAddress, satsPerKb: Math.floor(feeRate * 1000), additionalPayments: [], }; const result = await cancelOrdTokenListings(config); tx = result.tx; _spentOutpoints = result.spentOutpoints; } // Broadcast transaction const broadcastResult = await broadcastTransaction(tx); if (!broadcastResult.success) { throw { code: "BROADCAST_FAILED", message: broadcastResult.error || "Failed to broadcast cancellation transaction", }; } if (!broadcastResult.txid) { throw { code: "BROADCAST_FAILED", message: "Transaction broadcast succeeded but no transaction ID returned", }; } const result = { txid: broadcastResult.txid, fee: tx.inputs.reduce((sum, input) => { const sourceOutput = input.sourceTransaction?.outputs?.[input.sourceOutputIndex]; return sum + (sourceOutput?.satoshis || 0); }, 0) - tx.outputs.reduce((sum, output) => sum + (output.satoshis || 0), 0), cancelledPrice: listing.priceSats, assetType: listing.assetType, tokenAmount: listing.tokenAmount, }; return result; } catch (error) { if (error.code) { throw error; } throw { code: "TRANSACTION_FAILED", message: "Failed to cancel listing", details: error, }; } }, onSuccess: (data) => { onSuccess?.({ success: true, txid: data.txid }); }, onError: (error) => { console.error("Cancel listing failed:", error); onError?.(error); }, }); return { // Mutation functions cancelListing: cancelMutation.mutate, cancelListingAsync: cancelMutation.mutateAsync, // State isLoading: cancelMutation.isPending, isError: cancelMutation.isError, isSuccess: cancelMutation.isSuccess, error: cancelMutation.error, data: cancelMutation.data, // Reset reset: cancelMutation.reset, }; }