import React, { useState } from 'react'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { ArrowRight, Loader2, CheckCircle, XCircle } from 'lucide-react'; import { useToast } from '@/hooks/use-toast'; interface TokenBalance { assetId: number; symbol: string; name: string; balance: string; decimals: number; usdValue: number; } interface TransferModalProps { isOpen: boolean; onClose: () => void; selectedAsset?: TokenBalance | null; } type TokenType = 'HEZ' | 'PEZ' | 'USDT' | 'BTC' | 'ETH' | 'DOT'; interface Token { symbol: TokenType; name: string; assetId?: number; decimals: number; color: string; } // Token logo mapping const TOKEN_LOGOS: Record = { HEZ: '/tokens/HEZ.png', PEZ: '/tokens/PEZ.png', USDT: '/tokens/USDT.png', BTC: '/tokens/BTC.png', ETH: '/tokens/ETH.png', DOT: '/tokens/DOT.png', BNB: '/tokens/BNB.png', }; const TOKENS: Token[] = [ { symbol: 'HEZ', name: 'Hez Token', decimals: 12, color: 'from-green-600 to-yellow-400' }, { symbol: 'PEZ', name: 'Pez Token', assetId: 1, decimals: 12, color: 'from-blue-600 to-purple-400' }, { symbol: 'USDT', name: 'Tether USD', assetId: 1000, decimals: 6, color: 'from-green-500 to-green-600' }, { symbol: 'BTC', name: 'Bitcoin', assetId: 3, decimals: 8, color: 'from-orange-500 to-yellow-500' }, { symbol: 'ETH', name: 'Ethereum', assetId: 4, decimals: 18, color: 'from-purple-500 to-blue-500' }, { symbol: 'DOT', name: 'Polkadot', assetId: 5, decimals: 10, color: 'from-pink-500 to-red-500' }, ]; export const TransferModal: React.FC = ({ isOpen, onClose, selectedAsset }) => { const { api, assetHubApi, isApiReady, isAssetHubReady, selectedAccount } = usePezkuwi(); const { toast } = useToast(); const [selectedToken, setSelectedToken] = useState('HEZ'); const [recipient, setRecipient] = useState(''); const [amount, setAmount] = useState(''); const [isTransferring, setIsTransferring] = useState(false); const [txStatus, setTxStatus] = useState<'idle' | 'signing' | 'pending' | 'success' | 'error'>('idle'); const [txHash, setTxHash] = useState(''); // Use the provided selectedAsset or fall back to token selection const currentToken = selectedAsset ? { symbol: selectedAsset.symbol as TokenType, name: selectedAsset.name, assetId: selectedAsset.assetId, decimals: selectedAsset.decimals, color: selectedAsset.assetId === 0 ? 'from-green-600 to-yellow-400' : selectedAsset.assetId === 1000 ? 'from-emerald-500 to-teal-500' : 'from-cyan-500 to-blue-500', } : TOKENS.find(t => t.symbol === selectedToken) || TOKENS[0]; const handleTransfer = async () => { if (!api || !isApiReady || !selectedAccount) { toast({ title: "Error", description: "Wallet not connected", variant: "destructive", }); return; } // Check if Asset Hub transfer (PEZ, wUSDT, wHEZ are on Asset Hub) const isAssetHubTransfer = currentToken.symbol === 'PEZ' || currentToken.symbol === 'USDT' || currentToken.symbol === 'wUSDT' || currentToken.symbol === 'wHEZ' || currentToken.assetId === 1 || // PEZ currentToken.assetId === 2 || // wHEZ currentToken.assetId === 1000; // wUSDT if (isAssetHubTransfer && (!assetHubApi || !isAssetHubReady)) { toast({ title: "Error", description: "Asset Hub connection not ready. This token is on Asset Hub.", variant: "destructive", }); return; } if (!recipient || !amount) { toast({ title: "Error", description: "Please fill in all fields", variant: "destructive", }); return; } setIsTransferring(true); setTxStatus('signing'); try { // Import web3FromAddress to get the injector const { web3FromAddress } = await import('@pezkuwi/extension-dapp'); const injector = await web3FromAddress(selectedAccount.address); // Convert amount to smallest unit const amountInSmallestUnit = BigInt(parseFloat(amount) * Math.pow(10, currentToken.decimals)); let transfer; let targetApi = api; // Default to main chain API // Create appropriate transfer transaction based on token type // HEZ uses native token transfer (balances pallet on main chain) // PEZ, wHEZ, wUSDT use assets pallet on Asset Hub if (currentToken.assetId === undefined || (selectedToken === 'HEZ' && !selectedAsset)) { // Native HEZ token transfer on main chain transfer = api.tx.balances.transferKeepAlive(recipient, amountInSmallestUnit.toString()); } else if (isAssetHubTransfer) { // Asset Hub transfer (PEZ, wHEZ, wUSDT) targetApi = assetHubApi!; transfer = assetHubApi!.tx.assets.transfer(currentToken.assetId, recipient, amountInSmallestUnit.toString()); } else { // Other asset token transfers on main chain transfer = api.tx.assets.transfer(currentToken.assetId, recipient, amountInSmallestUnit.toString()); } setTxStatus('pending'); // Sign and send transaction const unsub = await transfer.signAndSend( selectedAccount.address, { signer: injector.signer }, ({ status, dispatchError }) => { if (status.isInBlock) { if (import.meta.env.DEV) console.log(`Transaction included in block: ${status.asInBlock}`); setTxHash(status.asInBlock.toHex()); } if (status.isFinalized) { if (import.meta.env.DEV) console.log(`Transaction finalized: ${status.asFinalized}`); // Check for errors if (dispatchError) { let errorMessage = 'Transaction failed'; if (dispatchError.isModule) { const decoded = targetApi.registry.findMetaError(dispatchError.asModule); errorMessage = `${decoded.section}.${decoded.name}: ${decoded.docs}`; } setTxStatus('error'); toast({ title: "Transfer Failed", description: errorMessage, variant: "destructive", }); } else { setTxStatus('success'); toast({ title: "Transfer Successful!", description: `Sent ${amount} ${currentToken.symbol} to ${recipient.slice(0, 8)}...${recipient.slice(-6)}`, }); // Reset form after 2 seconds setTimeout(() => { setRecipient(''); setAmount(''); setTxStatus('idle'); setTxHash(''); onClose(); }, 2000); } setIsTransferring(false); unsub(); } } ); } catch (error) { if (import.meta.env.DEV) console.error('Transfer error:', error); setTxStatus('error'); setIsTransferring(false); toast({ title: "Transfer Failed", description: error instanceof Error ? error.message : "An error occurred during transfer", variant: "destructive", }); } }; const handleClose = () => { if (!isTransferring) { setRecipient(''); setAmount(''); setTxStatus('idle'); setTxHash(''); setSelectedToken('HEZ'); onClose(); } }; return ( {selectedAsset ? `Send ${selectedAsset.symbol}` : 'Send Tokens'} {selectedAsset ? `Transfer ${selectedAsset.name} to another account` : 'Transfer tokens to another account'} {txStatus === 'success' ? (

Transfer Successful!

Your transaction has been finalized

{txHash && (
Transaction Hash
{txHash}
)}
) : txStatus === 'error' ? (

Transfer Failed

Please try again

) : (
{/* Token Selection - Only show if no asset is pre-selected */} {!selectedAsset && (
)}
setRecipient(e.target.value)} placeholder="Recipient address" className="bg-gray-800 border-gray-700 text-white mt-2 placeholder:text-gray-500 placeholder:opacity-50" disabled={isTransferring} />
setAmount(e.target.value)} placeholder="Amount" className="bg-gray-800 border-gray-700 text-white mt-2 placeholder:text-gray-500 placeholder:opacity-50" disabled={isTransferring} />
Decimals: {currentToken.decimals}
{txStatus === 'signing' && (

Please sign the transaction in your Pezkuwi.js extension

)} {txStatus === 'pending' && (

Transaction pending... Waiting for finalization

)}
)}
); };