{
  "name": "wallet-components-sendbsvbutton",
  "type": "registry:component",
  "dependencies": [
    "@radix-ui/react-icons"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/wallet/components/SendBSVButton.tsx",
      "type": "registry:component",
      "content": "import { ArrowUpIcon, Cross2Icon, PersonIcon } from \"@radix-ui/react-icons\";\n// SendBSVButton Component - Send BSV transactions\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport { useBitcoinAuth } from \"../../../hooks/useBitcoinAuth.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { Badge } from \"../../ui/badge.js\";\nimport { Button } from \"../../ui/button.js\";\nimport {\n\tDialog,\n\tDialogClose,\n\tDialogContent,\n\tDialogTitle,\n\tDialogTrigger,\n} from \"../../ui/dialog.js\";\nimport { Input } from \"../../ui/input.js\";\nimport { Label } from \"../../ui/label.js\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"../../ui/select.js\";\nimport { Textarea } from \"../../ui/textarea.js\";\nimport { useSendBSV } from \"../hooks/useSendBSV.js\";\nimport type { WalletUserExtension } from \"../types/extended-user.js\";\nimport { formatBSV, isValidAmount } from \"../utils/currency.js\";\n\nexport interface SendBSVButtonProps {\n\t// Pre-fill values\n\trecipientAddress?: string;\n\tamount?: string;\n\tmessage?: string;\n\n\t// Wallet data (optional - if not provided, will try to get from auth context)\n\twalletData?: WalletUserExtension;\n\n\t// Configuration\n\tdisabled?: boolean;\n\tloading?: boolean;\n\tclassName?: string;\n\tvariant?: \"default\" | \"secondary\" | \"outline\" | \"ghost\";\n\tsize?: \"sm\" | \"default\" | \"lg\";\n\tmaxAmount?: number; // In satoshis\n\tfeeRate?: number;\n\n\t// Callbacks\n\tonSuccess?: (txid: string) => void;\n\tonError?: (error: Error) => void;\n\n\t// UI customization\n\tbuttonText?: string;\n\tdialogTitle?: string;\n\tshowContacts?: boolean;\n}\n\nexport function SendBSVButton({\n\trecipientAddress: initialAddress = \"\",\n\tamount: initialAmount = \"\",\n\tmessage: initialMessage = \"\",\n\twalletData,\n\tdisabled = false,\n\tloading = false,\n\tclassName,\n\tvariant = \"default\",\n\tsize = \"default\",\n\tmaxAmount,\n\tfeeRate = 0.5,\n\tonSuccess,\n\tonError,\n\tbuttonText = \"Send BSV\",\n\tdialogTitle = \"Send BSV\",\n\tshowContacts = false,\n}: SendBSVButtonProps) {\n\tconst [open, setOpen] = useState(false);\n\tconst [address, setAddress] = useState(initialAddress);\n\tconst [amount, setAmount] = useState(initialAmount);\n\tconst [message, setMessage] = useState(initialMessage);\n\tconst [priority, setPriority] = useState<\"low\" | \"normal\" | \"high\">(\"normal\");\n\n\tconst { user, isAuthenticated } = useBitcoinAuth();\n\n\t// Use provided wallet data or fall back to auth user (which may not have wallet props)\n\tconst currentWalletData = walletData || (user as WalletUserExtension);\n\tconst hasWalletData = !!(\n\t\tcurrentWalletData?.balance && currentWalletData?.paymentKey\n\t);\n\n\tconst {\n\t\tsendBSV,\n\t\tisLoading: isSending,\n\t\testimatedFee,\n\t} = useSendBSV({\n\t\tfeeRate: priority === \"low\" ? 0.25 : priority === \"high\" ? 1.0 : feeRate,\n\t\twalletData: currentWalletData,\n\t\tonSuccess: (result) => {\n\t\t\tsetOpen(false);\n\t\t\tsetAddress(\"\");\n\t\t\tsetAmount(\"\");\n\t\t\tsetMessage(\"\");\n\t\t\tonSuccess?.(result.txid);\n\t\t},\n\t\tonError: (error) => {\n\t\t\tonError?.(new Error(error.message));\n\t\t},\n\t});\n\n\tconst handleSubmit = async (e: React.FormEvent) => {\n\t\te.preventDefault();\n\n\t\tif (!isAuthenticated || !user || !hasWalletData) {\n\t\t\tonError?.(new Error(\"Wallet data required for sending BSV\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// Validate address\n\t\tif (!address || address.length < 26) {\n\t\t\tonError?.(new Error(\"Please enter a valid Bitcoin address\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// Validate amount\n\t\tif (!isValidAmount(amount) || Number.parseFloat(amount) <= 0) {\n\t\t\tonError?.(new Error(\"Please enter a valid amount\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst amountSats = Math.floor(Number.parseFloat(amount) * 100000000);\n\n\t\t// Check maximum amount\n\t\tif (maxAmount && amountSats > maxAmount) {\n\t\t\tonError?.(\n\t\t\t\tnew Error(`Amount exceeds maximum of ${formatBSV(maxAmount)} BSV`),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check available balance\n\t\tconst availableBalance = currentWalletData?.balance?.confirmed || 0;\n\t\tif (amountSats + (estimatedFee || 500) > availableBalance) {\n\t\t\tonError?.(new Error(\"Insufficient balance for transaction + fees\"));\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tawait sendBSV({\n\t\t\t\ttoAddress: address,\n\t\t\t\tsatoshis: amountSats,\n\t\t\t\tmessage: message || undefined,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tonError?.(error as Error);\n\t\t}\n\t};\n\n\tconst handleMaxAmount = () => {\n\t\tif (!currentWalletData?.balance?.confirmed) return;\n\n\t\tconst fee = estimatedFee || 500;\n\t\tconst maxSendable = Math.max(0, currentWalletData.balance.confirmed - fee);\n\t\tconst maxBSV = maxSendable / 100000000;\n\t\tsetAmount(maxBSV.toFixed(8).replace(/\\.?0+$/, \"\"));\n\t};\n\n\tif (!isAuthenticated) {\n\t\treturn (\n\t\t\t<Button disabled variant=\"ghost\" size={size} className={className}>\n\t\t\t\tSign in to Send\n\t\t\t</Button>\n\t\t);\n\t}\n\n\tconst availableBalance = currentWalletData?.balance?.confirmed || 0;\n\tconst amountSats = amount\n\t\t? Math.floor(Number.parseFloat(amount) * 100000000)\n\t\t: 0;\n\tconst totalCost = amountSats + (estimatedFee || 500);\n\tconst canAfford = totalCost <= availableBalance;\n\n\treturn (\n\t\t<Dialog open={open} onOpenChange={setOpen}>\n\t\t\t<DialogTrigger asChild>\n\t\t\t\t<Button\n\t\t\t\t\tvariant={variant}\n\t\t\t\t\tsize={size}\n\t\t\t\t\tdisabled={disabled || loading || availableBalance === 0}\n\t\t\t\t\tclassName={className}\n\t\t\t\t>\n\t\t\t\t\t<ArrowUpIcon className=\"w-4 h-4 mr-2\" />\n\t\t\t\t\t{buttonText}\n\t\t\t\t</Button>\n\t\t\t</DialogTrigger>\n\n\t\t\t<DialogContent className=\"max-w-md\">\n\t\t\t\t<DialogTitle>{dialogTitle}</DialogTitle>\n\n\t\t\t\t<form onSubmit={handleSubmit}>\n\t\t\t\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t\t\t\t{/* Available Balance */}\n\t\t\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">Available Balance</p>\n\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">\n\t\t\t\t\t\t\t\t{formatBSV(availableBalance)} BSV\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Recipient Address */}\n\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t\t\t\t<Label htmlFor=\"recipient-address\">Send To</Label>\n\t\t\t\t\t\t\t\t{showContacts && (\n\t\t\t\t\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\">\n\t\t\t\t\t\t\t\t\t\t<PersonIcon className=\"w-3.5 h-3.5 mr-1\" />\n\t\t\t\t\t\t\t\t\t\tContacts\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tid=\"recipient-address\"\n\t\t\t\t\t\t\t\tplaceholder=\"Bitcoin address (1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa)\"\n\t\t\t\t\t\t\t\tvalue={address}\n\t\t\t\t\t\t\t\tonChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n\t\t\t\t\t\t\t\t\tsetAddress(e.target.value)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tclassName=\"font-mono text-xs\"\n\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Amount */}\n\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t\t\t\t<Label htmlFor=\"amount\">Amount</Label>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={handleMaxAmount}\n\t\t\t\t\t\t\t\t\tdisabled={availableBalance === 0}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tMax\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div className=\"relative\">\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tid=\"amount\"\n\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\tstep=\"0.00000001\"\n\t\t\t\t\t\t\t\t\tmin=\"0\"\n\t\t\t\t\t\t\t\t\tplaceholder=\"0.00000000\"\n\t\t\t\t\t\t\t\t\tvalue={amount}\n\t\t\t\t\t\t\t\t\tonChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n\t\t\t\t\t\t\t\t\t\tsetAmount(e.target.value)\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t\t\tclassName=\"pr-12\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<span className=\"absolute right-3 top-1/2 -translate-y-1/2 text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tBSV\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{amount && (\n\t\t\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t={\" \"}\n\t\t\t\t\t\t\t\t\t{Math.floor(\n\t\t\t\t\t\t\t\t\t\tNumber.parseFloat(amount) * 100000000,\n\t\t\t\t\t\t\t\t\t).toLocaleString()}{\" \"}\n\t\t\t\t\t\t\t\t\tsatoshis\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Transaction Priority */}\n\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t<Label htmlFor=\"priority\">Priority</Label>\n\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\tvalue={priority}\n\t\t\t\t\t\t\t\tonValueChange={(value: \"low\" | \"normal\" | \"high\") =>\n\t\t\t\t\t\t\t\t\tsetPriority(value)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<SelectTrigger id=\"priority\">\n\t\t\t\t\t\t\t\t\t<SelectValue />\n\t\t\t\t\t\t\t\t</SelectTrigger>\n\t\t\t\t\t\t\t\t<SelectContent>\n\t\t\t\t\t\t\t\t\t<SelectItem value=\"low\">\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex justify-between w-full\">\n\t\t\t\t\t\t\t\t\t\t\t<span>Low</span>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t~0.25 sat/byte\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</SelectItem>\n\t\t\t\t\t\t\t\t\t<SelectItem value=\"normal\">\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex justify-between w-full\">\n\t\t\t\t\t\t\t\t\t\t\t<span>Normal</span>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t~0.5 sat/byte\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</SelectItem>\n\t\t\t\t\t\t\t\t\t<SelectItem value=\"high\">\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex justify-between w-full\">\n\t\t\t\t\t\t\t\t\t\t\t<span>High</span>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t~1.0 sat/byte\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</SelectItem>\n\t\t\t\t\t\t\t\t</SelectContent>\n\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Message (Optional) */}\n\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t<Label htmlFor=\"message\">Message (Optional)</Label>\n\t\t\t\t\t\t\t<Textarea\n\t\t\t\t\t\t\t\tid=\"message\"\n\t\t\t\t\t\t\t\tplaceholder=\"Optional message for this transaction\"\n\t\t\t\t\t\t\t\tvalue={message}\n\t\t\t\t\t\t\t\tonChange={(e: React.ChangeEvent<HTMLTextAreaElement>) =>\n\t\t\t\t\t\t\t\t\tsetMessage(e.target.value)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tmaxLength={80}\n\t\t\t\t\t\t\t\tclassName=\"min-h-[60px]\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\tMessages are stored on-chain and are publicly visible\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Transaction Summary */}\n\t\t\t\t\t\t{amount && estimatedFee && (\n\t\t\t\t\t\t\t<div className=\"flex flex-col gap-2 p-3 bg-muted rounded-md\">\n\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">Transaction Summary</p>\n\t\t\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">Amount</p>\n\t\t\t\t\t\t\t\t\t<p className=\"text-xs\">{formatBSV(amountSats)} BSV</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">Network Fee</p>\n\t\t\t\t\t\t\t\t\t<p className=\"text-xs\">{formatBSV(estimatedFee)} BSV</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">Total</p>\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\t{formatBSV(totalCost)} BSV\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t{!canAfford && (\n\t\t\t\t\t\t\t\t\t<Badge variant=\"destructive\">Insufficient balance</Badge>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Actions */}\n\t\t\t\t\t\t<div className=\"flex gap-3 mt-4 justify-end\">\n\t\t\t\t\t\t\t<DialogClose asChild>\n\t\t\t\t\t\t\t\t<Button variant=\"secondary\">Cancel</Button>\n\t\t\t\t\t\t\t</DialogClose>\n\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={isSending || !address || !amount || !canAfford}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isSending ? \"Sending...\" : \"Send BSV\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</form>\n\n\t\t\t\t<DialogClose className=\"absolute top-2 right-2\">\n\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\" className=\"h-8 w-8 p-0\">\n\t\t\t\t\t\t<Cross2Icon className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</DialogClose>\n\t\t\t</DialogContent>\n\t\t</Dialog>\n\t);\n}\n\n// Quick send button for predefined amounts\nexport function QuickSendButton({\n\tamount,\n\trecipient,\n\tonSuccess,\n\tonError,\n\t...props\n}: Omit<SendBSVButtonProps, \"buttonText\" | \"dialogTitle\"> & {\n\tamount: string;\n\trecipient: string;\n}) {\n\treturn (\n\t\t<SendBSVButton\n\t\t\trecipientAddress={recipient}\n\t\t\tamount={amount}\n\t\t\tonSuccess={onSuccess}\n\t\t\tonError={onError}\n\t\t\tbuttonText={`Send ${amount} BSV`}\n\t\t\tdialogTitle={`Send ${amount} BSV`}\n\t\t\tsize=\"sm\"\n\t\t\tvariant=\"secondary\"\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n",
      "target": "<%- config.aliases.components %>/sendbsvbutton.tsx"
    }
  ]
}