diff --git a/web/src/components/dex/InitializeHezPoolModal.tsx b/web/src/components/dex/InitializeHezPoolModal.tsx index 3512be15..f1923363 100644 --- a/web/src/components/dex/InitializeHezPoolModal.tsx +++ b/web/src/components/dex/InitializeHezPoolModal.tsx @@ -1,12 +1,13 @@ 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 { X, AlertCircle, Loader2, CheckCircle, Info, ArrowDownUp } 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 { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { useToast } from '@/hooks/use-toast'; interface InitializeHezPoolModalProps { @@ -16,6 +17,7 @@ interface InitializeHezPoolModalProps { } type TransactionStatus = 'idle' | 'signing' | 'submitting' | 'success' | 'error'; +type WrapMode = 'wrap' | 'unwrap'; export const InitializeHezPoolModal: React.FC = ({ isOpen, @@ -27,7 +29,8 @@ export const InitializeHezPoolModal: React.FC = ({ const { account, signer } = useWallet(); const { toast } = useToast(); - const [hezAmount, setHezAmount] = useState('100000'); + const [mode, setMode] = useState('wrap'); + const [amount, setAmount] = useState('10000'); const [hezBalance, setHezBalance] = useState('0'); const [whezBalance, setWhezBalance] = useState('0'); @@ -36,15 +39,20 @@ export const InitializeHezPoolModal: React.FC = ({ const [txStatus, setTxStatus] = useState('idle'); const [errorMessage, setErrorMessage] = useState(''); - // Reset form when modal closes + // Reset form when modal closes or mode changes useEffect(() => { if (!isOpen) { - setHezAmount('100000'); + setAmount('10000'); setTxStatus('idle'); setErrorMessage(''); } }, [isOpen]); + useEffect(() => { + setTxStatus('idle'); + setErrorMessage(''); + }, [mode]); + // Check if tokenWrapper pallet is available on Asset Hub useEffect(() => { const checkPallet = async () => { @@ -56,14 +64,12 @@ export const InitializeHezPoolModal: React.FC = ({ try { // Check if tokenWrapper pallet exists on Asset Hub const hasTokenWrapper = assetHubApi.tx.tokenWrapper !== undefined && - assetHubApi.tx.tokenWrapper.wrap !== undefined; + assetHubApi.tx.tokenWrapper.wrap !== undefined && + assetHubApi.tx.tokenWrapper.unwrap !== undefined; setPalletAvailable(hasTokenWrapper); if (import.meta.env.DEV) { console.log('🔍 TokenWrapper pallet on Asset Hub:', hasTokenWrapper); - if (!hasTokenWrapper) { - console.log('Available pallets on Asset Hub:', Object.keys(assetHubApi.tx)); - } } } catch (error) { if (import.meta.env.DEV) console.error('Failed to check pallet:', error); @@ -82,21 +88,28 @@ export const InitializeHezPoolModal: React.FC = ({ try { // HEZ balance (native on Asset Hub) const balance = await assetHubApi.query.system.account(account); - const freeBalance = balance.data.free.toString(); + const freeBalance = (balance as { data: { free: { toString: () => string } } }).data.free.toString(); setHezBalance(freeBalance); // wHEZ balance (asset 2 on Asset Hub - tokenWrapper creates asset 2) const whezData = await assetHubApi.query.assets.account(2, account); - setWhezBalance(whezData.isSome ? whezData.unwrap().balance.toString() : '0'); + const whezTyped = whezData as { isSome: boolean; unwrap: () => { balance: { toString: () => string } } }; + setWhezBalance(whezTyped.isSome ? whezTyped.unwrap().balance.toString() : '0'); } catch (error) { if (import.meta.env.DEV) console.error('Failed to fetch balances from Asset Hub:', error); } }; fetchBalances(); - }, [assetHubApi, isAssetHubReady, account]); - const handleWrap = async () => { + // Refetch after successful transaction + if (txStatus === 'success') { + const timer = setTimeout(fetchBalances, 2000); + return () => clearTimeout(timer); + } + }, [assetHubApi, isAssetHubReady, account, txStatus]); + + const handleTransaction = async () => { if (!assetHubApi || !isAssetHubReady || !signer || !account) { toast({ title: 'Error', @@ -107,7 +120,7 @@ export const InitializeHezPoolModal: React.FC = ({ } if (!palletAvailable) { - setErrorMessage('TokenWrapper pallet is not available on Asset Hub. Please contact the team.'); + setErrorMessage('TokenWrapper pallet is not available on Asset Hub.'); toast({ title: 'Pallet Not Available', description: 'The TokenWrapper pallet is not deployed on Asset Hub.', @@ -116,15 +129,16 @@ export const InitializeHezPoolModal: React.FC = ({ return; } - const hezAmountRaw = BigInt(parseFloat(hezAmount) * 10 ** 12); + const amountRaw = BigInt(parseFloat(amount) * 10 ** 12); - if (hezAmountRaw <= BigInt(0)) { + if (amountRaw <= BigInt(0)) { setErrorMessage('Amount must be greater than zero'); return; } - if (hezAmountRaw > BigInt(hezBalance)) { - setErrorMessage('Insufficient HEZ balance on Asset Hub'); + const sourceBalance = mode === 'wrap' ? hezBalance : whezBalance; + if (amountRaw > BigInt(sourceBalance)) { + setErrorMessage(`Insufficient ${mode === 'wrap' ? 'HEZ' : 'wHEZ'} balance`); return; } @@ -132,16 +146,21 @@ export const InitializeHezPoolModal: React.FC = ({ setErrorMessage(''); try { - if (import.meta.env.DEV) console.log('🔄 Wrapping HEZ to wHEZ on Asset Hub...', { - hezAmount, - hezAmountRaw: hezAmountRaw.toString(), - }); + const isWrap = mode === 'wrap'; + if (import.meta.env.DEV) { + console.log(`🔄 ${isWrap ? 'Wrapping' : 'Unwrapping'} on Asset Hub...`, { + amount, + amountRaw: amountRaw.toString(), + }); + } - const wrapTx = assetHubApi.tx.tokenWrapper.wrap(hezAmountRaw.toString()); + const tx = isWrap + ? assetHubApi.tx.tokenWrapper.wrap(amountRaw.toString()) + : assetHubApi.tx.tokenWrapper.unwrap(amountRaw.toString()); setTxStatus('submitting'); - await wrapTx.signAndSend( + await tx.signAndSend( account, { signer }, ({ status, dispatchError, events }) => { @@ -170,28 +189,27 @@ export const InitializeHezPoolModal: React.FC = ({ variant: 'destructive', }); } else { - if (import.meta.env.DEV) console.log('✅ Wrap successful!'); + if (import.meta.env.DEV) console.log(`✅ ${isWrap ? 'Wrap' : 'Unwrap'} successful!`); if (import.meta.env.DEV) console.log('📋 Events:', events.map(e => e.event.method).join(', ')); setTxStatus('success'); toast({ title: 'Success!', - description: `Successfully wrapped ${hezAmount} HEZ to wHEZ`, + description: isWrap + ? `Successfully wrapped ${amount} HEZ to wHEZ` + : `Successfully unwrapped ${amount} wHEZ to HEZ`, }); - setTimeout(() => { - onSuccess?.(); - onClose(); - }, 2000); + onSuccess?.(); } } } ); } catch (error) { - if (import.meta.env.DEV) console.error('Wrap failed:', error); + if (import.meta.env.DEV) console.error('Transaction failed:', error); setErrorMessage(error instanceof Error ? error.message : 'Transaction failed'); setTxStatus('error'); toast({ title: 'Error', - description: error instanceof Error ? error.message : 'Wrap failed', + description: error instanceof Error ? error.message : 'Transaction failed', variant: 'destructive', }); } @@ -199,16 +217,22 @@ export const InitializeHezPoolModal: React.FC = ({ if (!isOpen) return null; - const hezBalanceDisplay = (parseFloat(hezBalance) / 10 ** 12).toFixed(4); - const whezBalanceDisplay = (parseFloat(whezBalance) / 10 ** 12).toFixed(4); + const hezBalanceDisplay = (parseFloat(hezBalance) / 10 ** 12).toLocaleString(undefined, { maximumFractionDigits: 4 }); + const whezBalanceDisplay = (parseFloat(whezBalance) / 10 ** 12).toLocaleString(undefined, { maximumFractionDigits: 4 }); + + const sourceToken = mode === 'wrap' ? 'HEZ' : 'wHEZ'; + const targetToken = mode === 'wrap' ? 'wHEZ' : 'HEZ'; + const sourceBalance = mode === 'wrap' ? hezBalanceDisplay : whezBalanceDisplay; + const sourceBalanceRaw = mode === 'wrap' ? hezBalance : whezBalance; return (
- - Wrap HEZ to wHEZ + + + Token Wrapping

- 💡 You will receive {hezAmount} wHEZ (1:1 ratio) + 💡 You will receive {amount} {targetToken} (1:1 ratio)

- {/* Current wHEZ Balance */} -
-
Current wHEZ Balance
-
- {whezBalanceDisplay} wHEZ -
-
- {/* Error Message */} {errorMessage && ( @@ -297,7 +349,7 @@ export const InitializeHezPoolModal: React.FC = ({ - Successfully wrapped {hezAmount} HEZ to wHEZ! + Successfully {mode === 'wrap' ? 'wrapped' : 'unwrapped'} {amount} {sourceToken} to {targetToken}! )} @@ -313,12 +365,11 @@ export const InitializeHezPoolModal: React.FC = ({ Cancel