import React, { useState, useEffect } from 'react'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { useWallet } from '@/contexts/WalletContext'; import { X, AlertCircle, Loader2, CheckCircle, Info } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { useToast } from '@/hooks/use-toast'; import type { AssetConfig } from './mintableAssets'; import { useTranslation } from 'react-i18next'; interface MintAssetModalProps { isOpen: boolean; onClose: () => void; onSuccess?: () => void; asset: AssetConfig; } type TransactionStatus = 'idle' | 'signing' | 'submitting' | 'success' | 'error'; export const MintAssetModal: React.FC = ({ isOpen, onClose, onSuccess, asset, }) => { const { assetHubApi, isAssetHubReady } = usePezkuwi(); const { account, signer } = useWallet(); const { toast } = useToast(); const { t } = useTranslation(); const [amount, setAmount] = useState(asset.defaultAmount || '1000'); const [balance, setBalance] = useState('0'); const [txStatus, setTxStatus] = useState('idle'); const [errorMessage, setErrorMessage] = useState(''); // Color schemes const colorSchemes: Record = { green: { bg: 'bg-green-500/10', border: 'border-green-500/30', text: 'text-green-400', button: 'bg-green-600', buttonHover: 'hover:bg-green-700' }, blue: { bg: 'bg-blue-500/10', border: 'border-blue-500/30', text: 'text-blue-400', button: 'bg-blue-600', buttonHover: 'hover:bg-blue-700' }, orange: { bg: 'bg-orange-500/10', border: 'border-orange-500/30', text: 'text-orange-400', button: 'bg-orange-600', buttonHover: 'hover:bg-orange-700' }, purple: { bg: 'bg-purple-500/10', border: 'border-purple-500/30', text: 'text-purple-400', button: 'bg-purple-600', buttonHover: 'hover:bg-purple-700' }, pink: { bg: 'bg-pink-500/10', border: 'border-pink-500/30', text: 'text-pink-400', button: 'bg-pink-600', buttonHover: 'hover:bg-pink-700' }, }; const colors = colorSchemes[asset.color || 'green']; // Reset form when modal closes or asset changes useEffect(() => { if (!isOpen) { setAmount(asset.defaultAmount || '1000'); setTxStatus('idle'); setErrorMessage(''); } }, [isOpen, asset]); // Fetch balance from Asset Hub useEffect(() => { const fetchBalance = async () => { if (!assetHubApi || !isAssetHubReady || !account) return; try { const assetData = await assetHubApi.query.assets.account(asset.id, account); setBalance(assetData.isSome ? assetData.unwrap().balance.toString() : '0'); } catch (error) { if (import.meta.env.DEV) console.error(`Failed to fetch ${asset.symbol} balance:`, error); } }; fetchBalance(); }, [assetHubApi, isAssetHubReady, account, asset.id, asset.symbol]); const handleMint = async () => { if (!assetHubApi || !isAssetHubReady || !signer || !account) { toast({ title: t('common.error'), description: t('mint.connectWallet'), variant: 'destructive', }); return; } if (!assetHubApi.tx.assets || !assetHubApi.tx.assets.mint) { setErrorMessage(t('mint.palletNotAvailable')); toast({ title: t('mint.palletToast'), description: t('mint.palletToastDesc'), variant: 'destructive', }); return; } const amountRaw = BigInt(Math.floor(parseFloat(amount) * 10 ** asset.decimals)); if (amountRaw <= BigInt(0)) { setErrorMessage(t('common.amountGtZero')); return; } setTxStatus('signing'); setErrorMessage(''); try { if (import.meta.env.DEV) console.log(`Minting ${asset.symbol} on Asset Hub...`, { amount, amountRaw: amountRaw.toString(), assetId: asset.id, }); const mintTx = assetHubApi.tx.assets.mint(asset.id, account, amountRaw.toString()); setTxStatus('submitting'); await mintTx.signAndSend( account, { signer }, ({ status, dispatchError }) => { if (import.meta.env.DEV) console.log('Transaction status:', status.type); if (status.isInBlock) { if (import.meta.env.DEV) console.log('In block:', status.asInBlock.toHex()); if (dispatchError) { let errorMsg = ''; if (dispatchError.isModule) { const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule); errorMsg = `${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`; if (import.meta.env.DEV) console.error('Module error:', errorMsg); } else { errorMsg = dispatchError.toString(); if (import.meta.env.DEV) console.error('Dispatch error:', errorMsg); } setErrorMessage(errorMsg); setTxStatus('error'); toast({ title: t('common.txFailed'), description: errorMsg, variant: 'destructive', }); } else { if (import.meta.env.DEV) console.log('Mint successful!'); setTxStatus('success'); toast({ title: t('common.success'), description: t('mint.minted', { amount, symbol: asset.symbol }), }); setTimeout(() => { onSuccess?.(); onClose(); }, 2000); } } } ); } catch (error) { if (import.meta.env.DEV) console.error('Mint failed:', error); setErrorMessage(error instanceof Error ? error.message : t('common.txFailed')); setTxStatus('error'); toast({ title: t('common.error'), description: error instanceof Error ? error.message : t('common.txFailed'), variant: 'destructive', }); } }; if (!isOpen) return null; const balanceDisplay = (parseFloat(balance) / 10 ** asset.decimals).toFixed(asset.decimals > 6 ? 6 : 2); return (
{asset.logo && ( {asset.symbol} )} {t('mint.title', { symbol: asset.symbol })}
{t('mint.adminOnly')}
{/* Info Banner */} {t('mint.info', { name: asset.name })} {/* Amount Input */}
{t('mint.current')}: {balanceDisplay} {asset.symbol}
setAmount(e.target.value)} placeholder="1000" className="w-full px-4 py-3 bg-gray-800 border border-gray-700 rounded-lg text-white text-lg" disabled={txStatus === 'signing' || txStatus === 'submitting'} />

{t('mint.decimalsInfo', { symbol: asset.symbol, decimals: asset.decimals })}

{/* Current Balance */}
{t('mint.currentBalance', { symbol: asset.symbol })}
{balanceDisplay} {asset.symbol}
{/* Error Message */} {errorMessage && ( {errorMessage} )} {/* Success Message */} {txStatus === 'success' && ( {t('mint.success', { amount, symbol: asset.symbol })} )} {/* Action Buttons */}
); };