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'; export interface AssetConfig { id: number; symbol: string; name: string; decimals: number; logo?: string; defaultAmount?: string; color?: string; // For theming (green, blue, orange, etc.) } 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 [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: 'Error', description: 'Please connect your wallet and wait for Asset Hub connection', variant: 'destructive', }); return; } if (!assetHubApi.tx.assets || !assetHubApi.tx.assets.mint) { setErrorMessage('Assets pallet is not available on Asset Hub.'); toast({ title: 'Pallet Not Available', description: 'The Assets pallet is not deployed on Asset Hub.', variant: 'destructive', }); return; } const amountRaw = BigInt(Math.floor(parseFloat(amount) * 10 ** asset.decimals)); if (amountRaw <= BigInt(0)) { setErrorMessage('Amount must be greater than zero'); 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, events }) => { 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: 'Transaction Failed', description: errorMsg, variant: 'destructive', }); } else { if (import.meta.env.DEV) console.log('Mint successful!'); setTxStatus('success'); toast({ title: 'Success!', description: `Successfully minted ${amount} ${asset.symbol}`, }); setTimeout(() => { onSuccess?.(); onClose(); }, 2000); } } } ); } catch (error) { if (import.meta.env.DEV) console.error('Mint failed:', error); setErrorMessage(error instanceof Error ? error.message : 'Transaction failed'); setTxStatus('error'); toast({ title: 'Error', description: error instanceof Error ? error.message : 'Mint failed', variant: 'destructive', }); } }; if (!isOpen) return null; const balanceDisplay = (parseFloat(balance) / 10 ** asset.decimals).toFixed(asset.decimals > 6 ? 6 : 2); return (
{asset.logo && ( {asset.symbol} )} Mint {asset.symbol}
Admin Only - Token Minting
{/* Info Banner */} Mint {asset.name} tokens for testing and liquidity pool creation. {/* Amount Input */}
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'} />

{asset.symbol} has {asset.decimals} decimals

{/* Current Balance */}
Current {asset.symbol} Balance
{balanceDisplay} {asset.symbol}
{/* Error Message */} {errorMessage && ( {errorMessage} )} {/* Success Message */} {txStatus === 'success' && ( Successfully minted {amount} {asset.symbol}! )} {/* Action Buttons */}
); }; // Pre-configured assets for easy use export const MINTABLE_ASSETS: Record = { wUSDT: { id: 1000, symbol: 'wUSDT', name: 'Wrapped USDT', decimals: 6, logo: '/shared/images/USDT(hez)logo.png', defaultAmount: '10000', color: 'green', }, wDOT: { id: 1001, symbol: 'wDOT', name: 'Wrapped DOT', decimals: 10, logo: '/shared/images/dot.png', defaultAmount: '100', color: 'pink', }, wETH: { id: 1002, symbol: 'wETH', name: 'Wrapped ETH', decimals: 18, logo: '/shared/images/etherium.png', defaultAmount: '10', color: 'purple', }, wBTC: { id: 1003, symbol: 'wBTC', name: 'Wrapped BTC', decimals: 8, logo: '/shared/images/bitcoin.png', defaultAmount: '1', color: 'orange', }, };