"use client"; import { useState, useEffect } from "react"; import Image from "next/image"; import { Button } from "@heroui/react"; import { useApp } from "@/contexts/AppContext"; import { buttonStyles } from "@/lib/buttonStyles"; import { useAccount } from "wagmi"; import { useLendingCollateral, useCollateralBalance, useWithdrawCollateral, useYTWalletBalance } from "@/hooks/useLendingCollateral"; import { useYTPrice } from "@/hooks/useCollateral"; import { notifySupplyCollateral, notifyWithdrawCollateral } from "@/lib/api/lending"; import { useAppKit } from "@reown/appkit/react"; import { toast } from "sonner"; import { getTxUrl } from "@/lib/contracts"; import { useTokenBySymbol } from "@/hooks/useTokenBySymbol"; const GRADIENT_COLORS: Record = { 'YT-A': 'linear-gradient(135deg, #FF8904 0%, #F54900 100%)', 'YT-B': 'linear-gradient(135deg, #00BBA7 0%, #007A55 100%)', 'YT-C': 'linear-gradient(135deg, #667EEA 0%, #764BA2 100%)', }; const DEFAULT_GRADIENT = 'linear-gradient(135deg, #6B7280 0%, #374151 100%)'; interface RepaySupplyCollateralProps { tokenType: string; onCollateralChanged?: () => void; } export default function RepaySupplyCollateral({ tokenType, onCollateralChanged }: RepaySupplyCollateralProps) { const { t } = useApp(); const { isConnected } = useAccount(); const { open } = useAppKit(); const [mounted, setMounted] = useState(false); const [activeTab, setActiveTab] = useState<'deposit' | 'withdraw'>('deposit'); const [amount, setAmount] = useState(''); useEffect(() => { setMounted(true); }, []); // On-chain data const ytToken = useTokenBySymbol(tokenType); const { formattedBalance, refetch: refetchBalance } = useCollateralBalance(ytToken); const { formattedBalance: walletBalance, refetch: refetchWalletBalance } = useYTWalletBalance(ytToken); const { formattedPrice } = useYTPrice(ytToken); const collateralUsd = (parseFloat(formattedBalance) * parseFloat(formattedPrice)).toFixed(2); // Deposit hook const { status: depositStatus, error: depositError, isLoading: isDepositing, approveHash: depositApproveHash, supplyHash, executeApproveAndSupply, reset: resetDeposit, } = useLendingCollateral(); // Withdraw hook const { status: withdrawStatus, error: withdrawError, isLoading: isWithdrawing, withdrawHash, executeWithdrawCollateral, reset: resetWithdraw, } = useWithdrawCollateral(); const DEPOSIT_TOAST_ID = 'collateral-deposit-tx'; const WITHDRAW_TOAST_ID = 'collateral-withdraw-tx'; // Deposit approve 交易提交 toast useEffect(() => { if (depositApproveHash && depositStatus === 'approving') { toast.loading(t("repay.toast.approvalSubmitted"), { id: DEPOSIT_TOAST_ID, description: t("repay.toast.waitingConfirmation"), action: { label: t("repay.toast.viewTx"), onClick: () => window.open(getTxUrl(depositApproveHash), '_blank') }, }); } }, [depositApproveHash, depositStatus]); // Deposit 交易提交 toast useEffect(() => { if (supplyHash && depositStatus === 'supplying') { toast.loading(t("repay.toast.depositSubmitted"), { id: DEPOSIT_TOAST_ID, description: t("repay.toast.depositingNow"), action: { label: t("repay.toast.viewTx"), onClick: () => window.open(getTxUrl(supplyHash), '_blank') }, }); } }, [supplyHash, depositStatus]); // Withdraw 交易提交 toast useEffect(() => { if (withdrawHash && withdrawStatus === 'withdrawing') { toast.loading(t("repay.toast.withdrawSubmitted"), { id: WITHDRAW_TOAST_ID, description: t("repay.toast.withdrawingNow"), action: { label: t("repay.toast.viewTx"), onClick: () => window.open(getTxUrl(withdrawHash), '_blank') }, }); } }, [withdrawHash, withdrawStatus]); // Notify backend after successful tx useEffect(() => { if (depositStatus === 'success' && supplyHash && amount) { toast.success(t("repay.toast.depositSuccess"), { id: DEPOSIT_TOAST_ID, description: t("repay.toast.depositSuccessDesc"), duration: 5000, }); notifySupplyCollateral(tokenType, amount, supplyHash); refetchBalance(); refetchWalletBalance(); onCollateralChanged?.(); setAmount(''); setTimeout(resetDeposit, 3000); } }, [depositStatus]); useEffect(() => { if (withdrawStatus === 'success' && withdrawHash && amount) { toast.success(t("repay.toast.withdrawSuccess"), { id: WITHDRAW_TOAST_ID, description: t("repay.toast.withdrawSuccessDesc"), duration: 5000, }); notifyWithdrawCollateral(tokenType, amount, withdrawHash); refetchBalance(); setAmount(''); setTimeout(resetWithdraw, 3000); } }, [withdrawStatus]); // 错误提示 useEffect(() => { if (depositError) { if (depositError === 'Transaction cancelled') { toast.dismiss(DEPOSIT_TOAST_ID); } else { toast.error(t("repay.toast.depositFailed"), { id: DEPOSIT_TOAST_ID, duration: 5000 }); } } }, [depositError]); useEffect(() => { if (withdrawError) { if (withdrawError === 'Transaction cancelled') { toast.dismiss(WITHDRAW_TOAST_ID); } else { toast.error(t("repay.toast.withdrawFailed"), { id: WITHDRAW_TOAST_ID, duration: 5000 }); } } }, [withdrawError]); const inputDecimals = ytToken?.onChainDecimals ?? ytToken?.decimals ?? 18; const displayDecimals = Math.min(inputDecimals, 6); const truncateDecimals = (s: string, d: number) => { const p = s.split('.'); return p.length > 1 ? `${p[0]}.${p[1].slice(0, d)}` : s; }; const isValidAmount = (v: string) => v !== '' && !isNaN(parseFloat(v)) && parseFloat(v) > 0; const handleAmountChange = (value: string) => { if (value === '') { setAmount(value); return; } if (!/^\d*\.?\d*$/.test(value)) return; const parts = value.split('.'); if (parts.length > 1 && parts[1].length > displayDecimals) return; const maxBalance = activeTab === 'deposit' ? parseFloat(walletBalance) : parseFloat(formattedBalance); if (parseFloat(value) > maxBalance) { setAmount(truncateDecimals(activeTab === 'deposit' ? walletBalance : formattedBalance, displayDecimals)); return; } setAmount(value); }; const handleAction = () => { if (!amount || parseFloat(amount) <= 0 || !ytToken) return; if (activeTab === 'deposit') { executeApproveAndSupply(ytToken, amount); } else { executeWithdrawCollateral(ytToken, amount); } }; const handleMax = () => { if (activeTab === 'deposit') { setAmount(truncateDecimals(walletBalance, displayDecimals)); } else { setAmount(truncateDecimals(formattedBalance, displayDecimals)); } }; const isLoading = isDepositing || isWithdrawing; const currentStatus = activeTab === 'deposit' ? depositStatus : withdrawStatus; const getButtonLabel = () => { if (!isConnected) return t("common.connectWallet"); if (currentStatus === 'approving') return t("common.approving"); if (currentStatus === 'approved') return t("repay.confirmedDepositing"); if (currentStatus === 'supplying') return t("repay.depositing"); if (currentStatus === 'withdrawing') return t("repay.withdrawing"); if (currentStatus === 'success') return t("common.success"); if (currentStatus === 'error') return t("common.failed"); if (amount && !isValidAmount(amount)) return t("common.invalidAmount"); return activeTab === 'deposit' ? t("repay.deposit") : t("repay.withdraw"); }; return (
{/* Title */}

{t("repay.supplyCollateral")}

{/* Token Info and Value */}
{ytToken?.iconUrl ? ( {tokenType} ) : ( tokenType.replace('YT-', '') )}
{!mounted ? '--' : parseFloat(formattedBalance).toLocaleString('en-US', { maximumFractionDigits: displayDecimals })} {tokenType}
{!mounted ? '--' : `$${parseFloat(collateralUsd).toLocaleString('en-US', { maximumFractionDigits: 2 })}`}
{/* Tab Switcher */}
{/* Amount Input */}
{t("repay.amount")} {activeTab === 'deposit' && mounted && ( )} {activeTab === 'withdraw' && mounted && ( )}
handleAmountChange(e.target.value)} className="flex-1 bg-transparent text-body-default font-bold text-text-primary dark:text-white outline-none" /> {tokenType}
{/* Action Button */}
); }