import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Wallet, TrendingUp, RefreshCw, Award, Plus, Coins, Send, Shield, Users, Fuel, Lock } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { ASSET_IDS, getAssetSymbol } from '@pezkuwi/lib/wallet'; import { AddTokenModal } from './AddTokenModal'; import { TransferModal } from './TransferModal'; import { XCMTeleportModal } from './XCMTeleportModal'; import { LPStakeModal } from './LPStakeModal'; import { getAllScores, type UserScores } from '@pezkuwi/lib/scores'; interface TokenBalance { assetId: number; symbol: string; name: string; balance: string; decimals: number; usdValue: number; isLpToken?: boolean; // LP tokens from poolAssets pallet } export const AccountBalance: React.FC = () => { const { t } = useTranslation(); const { api, assetHubApi, peopleApi, isApiReady, isAssetHubReady, isPeopleReady, selectedAccount } = usePezkuwi(); const [balance, setBalance] = useState<{ free: string; reserved: string; total: string; }>({ free: '0', reserved: '0', total: '0', }); // HEZ balances on different chains const [assetHubHezBalance, setAssetHubHezBalance] = useState('0'); const [peopleHezBalance, setPeopleHezBalance] = useState('0'); const [pezBalance, setPezBalance] = useState('0'); const [usdtBalance, setUsdtBalance] = useState('0'); const [whezBalance, setWhezBalance] = useState('0'); const [lpTokens, setLpTokens] = useState([]); const [hezUsdPrice, setHezUsdPrice] = useState(0); const [pezUsdPrice, setPezUsdPrice] = useState(0); const [scores, setScores] = useState({ trustScore: 0, referralScore: 0, stakingScore: 0, tikiScore: 0, totalScore: 0 }); const [loadingScores, setLoadingScores] = useState(false); const [isLoading, setIsLoading] = useState(false); const [otherTokens, setOtherTokens] = useState([]); const [isAddTokenModalOpen, setIsAddTokenModalOpen] = useState(false); const [isTransferModalOpen, setIsTransferModalOpen] = useState(false); const [isXCMTeleportModalOpen, setIsXCMTeleportModalOpen] = useState(false); const [selectedTokenForTransfer, setSelectedTokenForTransfer] = useState(null); const [customTokenIds, setCustomTokenIds] = useState(() => { const stored = localStorage.getItem('customTokenIds'); return stored ? JSON.parse(stored) : []; }); const [isLPStakeModalOpen, setIsLPStakeModalOpen] = useState(false); const [selectedLPForStake, setSelectedLPForStake] = useState(null); // Helper function to get asset decimals const getAssetDecimals = (assetId: number): number => { if (assetId === ASSET_IDS.WUSDT) return 6; // wUSDT has 6 decimals return 12; // wHEZ, PEZ and others have 12 decimals by default }; // Helper to decode hex string to UTF-8 const hexToString = (hex: string): string => { if (!hex || hex === '0x') return ''; try { const hexStr = hex.startsWith('0x') ? hex.slice(2) : hex; const bytes = new Uint8Array(hexStr.match(/.{1,2}/g)?.map(byte => parseInt(byte, 16)) || []); return new TextDecoder('utf-8').decode(bytes).replace(/\0/g, ''); } catch { return ''; } }; // Token logo mapping const TOKEN_LOGOS: Record = { HEZ: '/tokens/HEZ.png', PEZ: '/tokens/PEZ.png', USDT: '/tokens/USDT.png', wUSDT: '/tokens/USDT.png', wHEZ: '/tokens/HEZ.png', // wHEZ uses same logo as HEZ BNB: '/tokens/BNB.png', BTC: '/tokens/BTC.png', DOT: '/tokens/DOT.png', ETH: '/tokens/ETH.png', 'HEZ-PEZ LP': '/tokens/LP.png', 'HEZ-USDT LP': '/tokens/LP.png', 'HEZ-DOT LP': '/tokens/LP.png', }; // Get token logo URL const getTokenLogo = (symbol: string): string | null => { return TOKEN_LOGOS[symbol] || TOKEN_LOGOS[symbol.toUpperCase()] || null; }; // Get token color based on assetId const getTokenColor = (assetId: number) => { const colors = { [ASSET_IDS.WHEZ]: { bg: 'from-green-500/20 to-yellow-500/20', text: 'text-green-400', border: 'border-green-500/30' }, [ASSET_IDS.WUSDT]: { bg: 'from-emerald-500/20 to-teal-500/20', text: 'text-emerald-400', border: 'border-emerald-500/30' }, }; return colors[assetId] || { bg: 'from-cyan-500/20 to-blue-500/20', text: 'text-cyan-400', border: 'border-cyan-500/30' }; }; // Fetch token prices from CoinGecko with fallback logic // Priority: CoinGecko direct > DOT-based calculation > DEX pool const fetchTokenPrices = async () => { try { if (import.meta.env.DEV) console.log('💰 Fetching token prices from CoinGecko...'); // CoinGecko API - fetch DOT, HEZ, PEZ prices // Note: HEZ and PEZ may not be listed yet, so we use DOT as fallback const coingeckoIds = 'polkadot,pezkuwichain,pez-token'; // DOT is always available // Use our proxy to avoid CORS and rate limits const response = await fetch( `https://api.pezkuwichain.io/api/prices?ids=${coingeckoIds}&vs_currencies=usd&include_24hr_change=true` ); let hezPrice = 0; let pezPrice = 0; if (response.ok) { const data = await response.json(); if (import.meta.env.DEV) console.log('📊 CoinGecko response:', data); const dotPrice = data['polkadot']?.usd || 0; const directHezPrice = data['pezkuwichain']?.usd || 0; const directPezPrice = data['pez-token']?.usd || 0; // Use direct CoinGecko price if available, otherwise calculate from DOT if (directHezPrice > 0) { hezPrice = directHezPrice; if (import.meta.env.DEV) console.log('✅ HEZ price from CoinGecko:', hezPrice, 'USD'); } else if (dotPrice > 0) { // HEZ = DOT / 3 hezPrice = dotPrice / 3; if (import.meta.env.DEV) console.log('✅ HEZ price (DOT/3):', hezPrice, 'USD'); } if (directPezPrice > 0) { pezPrice = directPezPrice; if (import.meta.env.DEV) console.log('✅ PEZ price from CoinGecko:', pezPrice, 'USD'); } else if (dotPrice > 0) { // PEZ = DOT / 10 pezPrice = dotPrice / 10; if (import.meta.env.DEV) console.log('✅ PEZ price (DOT/10):', pezPrice, 'USD'); } } // If CoinGecko failed or returned 0, try DEX pool as fallback if ((hezPrice === 0 || pezPrice === 0) && api && isApiReady) { if (import.meta.env.DEV) console.log('⚠️ CoinGecko incomplete, trying DEX pool fallback...'); await fetchDexPoolPrices(hezPrice, pezPrice); } else { setHezUsdPrice(hezPrice); setPezUsdPrice(pezPrice); } } catch (error) { if (import.meta.env.DEV) console.error('❌ CoinGecko fetch failed, trying DEX pool:', error); // Fallback to DEX pool prices if (api && isApiReady) { await fetchDexPoolPrices(0, 0); } } }; // Fallback: Fetch prices from DEX pools const fetchDexPoolPrices = async (existingHezPrice: number, existingPezPrice: number) => { if (!api || !isApiReady) return; try { const { stringToU8a } = await import('@pezkuwi/util'); const { blake2AsU8a } = await import('@pezkuwi/util-crypto'); const PALLET_ID = stringToU8a('py/ascon'); let hezPrice = existingHezPrice; let pezPrice = existingPezPrice; // Only fetch HEZ from DEX if not already set if (hezPrice === 0) { const whezPoolId = api.createType('(u32, u32)', [ASSET_IDS.WHEZ, ASSET_IDS.WUSDT]); const whezPalletIdType = api.createType('[u8; 8]', PALLET_ID); const whezFullTuple = api.createType('([u8; 8], (u32, u32))', [whezPalletIdType, whezPoolId]); const whezAccountHash = blake2AsU8a(whezFullTuple.toU8a(), 256); const whezPoolAccountId = api.createType('AccountId32', whezAccountHash); const whezReserve0Query = await api.query.assets.account(ASSET_IDS.WHEZ, whezPoolAccountId); const whezReserve1Query = await api.query.assets.account(ASSET_IDS.WUSDT, whezPoolAccountId); if (whezReserve0Query.isSome && whezReserve1Query.isSome) { const reserve0Data = whezReserve0Query.unwrap(); const reserve1Data = whezReserve1Query.unwrap(); const reserve0 = BigInt(reserve0Data.balance.toString()); const reserve1 = BigInt(reserve1Data.balance.toString()); hezPrice = Number(reserve1 * BigInt(10 ** 12)) / Number(reserve0 * BigInt(10 ** 6)); if (import.meta.env.DEV) console.log('✅ HEZ price from DEX:', hezPrice, 'USD'); } } // Only fetch PEZ from DEX if not already set if (pezPrice === 0) { const pezPoolId = api.createType('(u32, u32)', [1, ASSET_IDS.WUSDT]); const pezPalletIdType = api.createType('[u8; 8]', PALLET_ID); const pezFullTuple = api.createType('([u8; 8], (u32, u32))', [pezPalletIdType, pezPoolId]); const pezAccountHash = blake2AsU8a(pezFullTuple.toU8a(), 256); const pezPoolAccountId = api.createType('AccountId32', pezAccountHash); const pezReserve0Query = await api.query.assets.account(1, pezPoolAccountId); const pezReserve1Query = await api.query.assets.account(ASSET_IDS.WUSDT, pezPoolAccountId); if (pezReserve0Query.isSome && pezReserve1Query.isSome) { const reserve0Data = pezReserve0Query.unwrap(); const reserve1Data = pezReserve1Query.unwrap(); const reserve0 = BigInt(reserve0Data.balance.toString()); const reserve1 = BigInt(reserve1Data.balance.toString()); pezPrice = Number(reserve1 * BigInt(10 ** 12)) / Number(reserve0 * BigInt(10 ** 6)); if (import.meta.env.DEV) console.log('✅ PEZ price from DEX:', pezPrice, 'USD'); } } setHezUsdPrice(hezPrice); setPezUsdPrice(pezPrice); } catch (error) { if (import.meta.env.DEV) console.error('❌ DEX pool price fetch failed:', error); } }; // Fetch other tokens (only custom tokens - wrapped tokens are backend-only) // IMPORTANT: Assets are on Asset Hub, use assetHubApi (not relay chain api) const fetchOtherTokens = async () => { if (!assetHubApi || !isAssetHubReady || !selectedAccount) return; try { const tokens: TokenBalance[] = []; // IMPORTANT: Only show custom tokens added by user // Core tokens are shown in their own dedicated cards - exclude them here // Using hardcoded IDs to avoid env variable issues const excludedAssetIds = [ 1, // PEZ 2, // wHEZ 3, // Old USDT (deprecated) 1000, // wUSDT (USDT) ]; const assetIdsToCheck = customTokenIds.filter((id) => !excludedAssetIds.includes(id)); for (const assetId of assetIdsToCheck) { try { // First check if asset exists on blockchain const assetInfo = await assetHubApi.query.assets.asset(assetId); if (!assetInfo || assetInfo.isNone) { // Asset doesn't exist on blockchain - skip it if (import.meta.env.DEV) console.log(`Asset ${assetId} not found on blockchain, skipping`); continue; } // Asset exists - get metadata const assetMetadata = await assetHubApi.query.assets.metadata(assetId); const metadata = assetMetadata.toJSON() as { symbol?: string; name?: string; decimals?: number }; // Decode hex strings properly let symbol = metadata.symbol || ''; let name = metadata.name || ''; if (typeof symbol === 'string' && symbol.startsWith('0x')) { symbol = hexToString(symbol); } if (typeof name === 'string' && name.startsWith('0x')) { name = hexToString(name); } // Fallback to known symbols if metadata is empty if (!symbol || symbol.trim() === '') { symbol = getAssetSymbol(assetId); } if (!name || name.trim() === '') { name = symbol; } const decimals = metadata.decimals || getAssetDecimals(assetId); // Get balance (may be 0 if user hasn't received any) let balanceFormatted = '0'; const assetBalance = await assetHubApi.query.assets.account(assetId, selectedAccount.address); if (assetBalance.isSome) { const assetData = assetBalance.unwrap(); const balance = assetData.balance.toString(); balanceFormatted = (parseInt(balance) / Math.pow(10, decimals)).toFixed(6); } // Simple USD calculation (would use real price feed in production) let usdValue = 0; if (assetId === ASSET_IDS.WUSDT) { usdValue = parseFloat(balanceFormatted); // 1 wUSDT = 1 USD } // Only show tokens that exist on blockchain tokens.push({ assetId, symbol: symbol.trim(), name: name.trim(), balance: balanceFormatted, decimals, usdValue }); } catch (error) { if (import.meta.env.DEV) console.error(`Failed to fetch token ${assetId}:`, error); } } setOtherTokens(tokens); } catch (error) { if (import.meta.env.DEV) console.error('Failed to fetch other tokens:', error); } }; const fetchBalance = async () => { if (!api || !isApiReady || !selectedAccount) return; setIsLoading(true); try { // Fetch HEZ balance const { data: balanceData } = await api.query.system.account(selectedAccount.address); const free = balanceData.free.toString(); const reserved = balanceData.reserved.toString(); // Convert from plancks to tokens (12 decimals) const decimals = 12; const divisor = Math.pow(10, decimals); const freeTokens = (parseInt(free) / divisor).toFixed(4); const reservedTokens = (parseInt(reserved) / divisor).toFixed(4); const totalTokens = ((parseInt(free) + parseInt(reserved)) / divisor).toFixed(4); setBalance({ free: freeTokens, reserved: reservedTokens, total: totalTokens, }); // Fetch HEZ balance on Asset Hub (for PEZ transfer fees) try { if (assetHubApi && isAssetHubReady) { const { data: assetHubBalanceData } = await assetHubApi.query.system.account(selectedAccount.address); const assetHubFree = assetHubBalanceData.free.toString(); const assetHubHezTokens = (parseInt(assetHubFree) / divisor).toFixed(4); setAssetHubHezBalance(assetHubHezTokens); } else { setAssetHubHezBalance('0.0000'); } } catch (error) { if (import.meta.env.DEV) console.error('Failed to fetch Asset Hub HEZ balance:', error); setAssetHubHezBalance('0.0000'); } // Fetch HEZ balance on People Chain (for identity/KYC fees) try { if (peopleApi && isPeopleReady) { const { data: peopleBalanceData } = await peopleApi.query.system.account(selectedAccount.address); const peopleFree = peopleBalanceData.free.toString(); const peopleHezTokens = (parseInt(peopleFree) / divisor).toFixed(4); setPeopleHezBalance(peopleHezTokens); } else { setPeopleHezBalance('0.0000'); } } catch (error) { if (import.meta.env.DEV) console.error('Failed to fetch People Chain HEZ balance:', error); setPeopleHezBalance('0.0000'); } // Fetch PEZ balance (Asset ID: 1) from Asset Hub try { if (assetHubApi && isAssetHubReady) { const pezAssetBalance = await assetHubApi.query.assets.account(1, selectedAccount.address); if (pezAssetBalance.isSome) { const assetData = pezAssetBalance.unwrap(); const pezAmount = assetData.balance.toString(); const pezTokens = (parseInt(pezAmount) / divisor).toFixed(4); setPezBalance(pezTokens); } else { setPezBalance('0.0000'); } } else { if (import.meta.env.DEV) console.log('Asset Hub not ready, PEZ balance pending...'); setPezBalance('0.0000'); } } catch (error) { if (import.meta.env.DEV) console.error('Failed to fetch PEZ balance from Asset Hub:', error); setPezBalance('0.0000'); } // Fetch USDT balance (wUSDT - Asset ID: 1000) from Asset Hub try { if (assetHubApi && isAssetHubReady) { const usdtAssetBalance = await assetHubApi.query.assets.account(ASSET_IDS.WUSDT, selectedAccount.address); if (usdtAssetBalance.isSome) { const assetData = usdtAssetBalance.unwrap(); const usdtAmount = assetData.balance.toString(); const usdtDecimals = 6; // wUSDT has 6 decimals const usdtDivisor = Math.pow(10, usdtDecimals); const usdtTokens = (parseInt(usdtAmount) / usdtDivisor).toFixed(2); setUsdtBalance(usdtTokens); } else { setUsdtBalance('0'); } } else { if (import.meta.env.DEV) console.log('Asset Hub not ready, wUSDT balance pending...'); setUsdtBalance('0'); } } catch (error) { if (import.meta.env.DEV) console.error('Failed to fetch USDT balance:', error); setUsdtBalance('0'); } // Fetch wHEZ balance (Asset ID: 2) from Asset Hub try { if (assetHubApi && isAssetHubReady) { const whezAssetBalance = await assetHubApi.query.assets.account(ASSET_IDS.WHEZ, selectedAccount.address); if (whezAssetBalance.isSome) { const assetData = whezAssetBalance.unwrap(); const whezAmount = assetData.balance.toString(); const whezTokens = (parseInt(whezAmount) / divisor).toFixed(4); setWhezBalance(whezTokens); } else { setWhezBalance('0'); } } else { if (import.meta.env.DEV) console.log('Asset Hub not ready, wHEZ balance pending...'); setWhezBalance('0'); } } catch (error) { if (import.meta.env.DEV) console.error('Failed to fetch wHEZ balance:', error); setWhezBalance('0'); } // Fetch LP Token balances from poolAssets pallet on Asset Hub try { if (assetHubApi && isAssetHubReady) { const lpTokensData: TokenBalance[] = []; // HEZ-PEZ LP Token (ID: 0) const hezPezLp = await assetHubApi.query.poolAssets.account(0, selectedAccount.address); if (hezPezLp.isSome) { const lpBalance = hezPezLp.unwrap().balance.toString(); const lpTokens = (parseInt(lpBalance) / divisor).toFixed(4); if (parseFloat(lpTokens) > 0) { lpTokensData.push({ assetId: 0, symbol: 'HEZ-PEZ LP', name: 'HEZ-PEZ Liquidity', balance: lpTokens, decimals: 12, usdValue: 0, // TODO: Calculate LP value isLpToken: true, }); } } // HEZ-USDT LP Token (ID: 1) const hezUsdtLp = await assetHubApi.query.poolAssets.account(1, selectedAccount.address); if (hezUsdtLp.isSome) { const lpBalance = hezUsdtLp.unwrap().balance.toString(); const lpTokens = (parseInt(lpBalance) / divisor).toFixed(4); if (parseFloat(lpTokens) > 0) { lpTokensData.push({ assetId: 1, symbol: 'HEZ-USDT LP', name: 'HEZ-USDT Liquidity', balance: lpTokens, decimals: 12, usdValue: 0, // TODO: Calculate LP value isLpToken: true, }); } } // HEZ-DOT LP Token (ID: 2) const hezDotLp = await assetHubApi.query.poolAssets.account(2, selectedAccount.address); if (hezDotLp.isSome) { const lpBalance = hezDotLp.unwrap().balance.toString(); const lpTokens = (parseInt(lpBalance) / divisor).toFixed(4); if (parseFloat(lpTokens) > 0) { lpTokensData.push({ assetId: 2, symbol: 'HEZ-DOT LP', name: 'HEZ-DOT Liquidity', balance: lpTokens, decimals: 12, usdValue: 0, // TODO: Calculate LP value isLpToken: true, }); } } setLpTokens(lpTokensData); if (import.meta.env.DEV) console.log('✅ LP tokens fetched:', lpTokensData); } } catch (error) { if (import.meta.env.DEV) console.error('Failed to fetch LP token balances:', error); setLpTokens([]); } // Fetch token prices from pools await fetchTokenPrices(); // Fetch other tokens await fetchOtherTokens(); } catch (error) { if (import.meta.env.DEV) console.error('Failed to fetch balance:', error); } finally { setIsLoading(false); } }; // Add custom token handler const handleAddToken = async (assetId: number) => { if (customTokenIds.includes(assetId)) { alert(t('balance.tokenAlreadyAdded')); return; } // Update custom tokens list const updatedTokenIds = [...customTokenIds, assetId]; setCustomTokenIds(updatedTokenIds); localStorage.setItem('customTokenIds', JSON.stringify(updatedTokenIds)); // Fetch the new token await fetchOtherTokens(); setIsAddTokenModalOpen(false); }; // Remove token handler (unused but kept for future feature) // const handleRemoveToken = (assetId: number) => { // const updatedTokenIds = customTokenIds.filter(id => id !== assetId); // setCustomTokenIds(updatedTokenIds); // localStorage.setItem('customTokenIds', JSON.stringify(updatedTokenIds)); // // // Remove from displayed tokens // setOtherTokens(prev => prev.filter(t => t.assetId !== assetId)); // }; useEffect(() => { fetchBalance(); fetchTokenPrices(); // Fetch token USD prices from pools // Fetch All Scores from blockchain with frontend fallback const fetchAllScores = async () => { if (!api || !isApiReady || !selectedAccount?.address) { setScores({ trustScore: 0, referralScore: 0, stakingScore: 0, tikiScore: 0, totalScore: 0 }); return; } setLoadingScores(true); try { // Use fallback function: peopleApi for on-chain scores, api (Relay) for staking data const userScores = await getAllScores(peopleApi || null, selectedAccount.address); setScores(userScores); } catch (err) { if (import.meta.env.DEV) console.error('Failed to fetch scores:', err); setScores({ trustScore: 0, referralScore: 0, stakingScore: 0, tikiScore: 0, totalScore: 0 }); } finally { setLoadingScores(false); } }; fetchAllScores(); // Subscribe to HEZ balance updates let unsubscribeHez: () => void; let unsubscribePez: () => void; let unsubscribeUsdt: () => void; const subscribeBalance = async () => { if (!api || !isApiReady || !selectedAccount) return; // Subscribe to HEZ balance unsubscribeHez = await api.query.system.account( selectedAccount.address, ({ data: balanceData }) => { const free = balanceData.free.toString(); const reserved = balanceData.reserved.toString(); const decimals = 12; const divisor = Math.pow(10, decimals); const freeTokens = (parseInt(free) / divisor).toFixed(4); const reservedTokens = (parseInt(reserved) / divisor).toFixed(4); const totalTokens = ((parseInt(free) + parseInt(reserved)) / divisor).toFixed(4); setBalance({ free: freeTokens, reserved: reservedTokens, total: totalTokens, }); } ); // Subscribe to PEZ balance (Asset ID: 1) from Asset Hub if (assetHubApi && isAssetHubReady) { try { unsubscribePez = await assetHubApi.query.assets.account( 1, selectedAccount.address, (assetBalance) => { if (assetBalance.isSome) { const assetData = assetBalance.unwrap(); const pezAmount = assetData.balance.toString(); const decimals = 12; const divisor = Math.pow(10, decimals); const pezTokens = (parseInt(pezAmount) / divisor).toFixed(4); setPezBalance(pezTokens); } else { setPezBalance('0.0000'); } } ); } catch (error) { if (import.meta.env.DEV) console.error('Failed to subscribe to PEZ balance from Asset Hub:', error); } } // Subscribe to USDT balance (wUSDT - Asset ID: 1000) from Asset Hub if (assetHubApi && isAssetHubReady) { try { unsubscribeUsdt = await assetHubApi.query.assets.account( ASSET_IDS.WUSDT, selectedAccount.address, (assetBalance) => { if (assetBalance.isSome) { const assetData = assetBalance.unwrap(); const usdtAmount = assetData.balance.toString(); const decimals = 6; // wUSDT has 6 decimals const divisor = Math.pow(10, decimals); const usdtTokens = (parseInt(usdtAmount) / divisor).toFixed(2); setUsdtBalance(usdtTokens); } else { setUsdtBalance('0'); } } ); } catch (error) { if (import.meta.env.DEV) console.error('Failed to subscribe to USDT balance:', error); } } }; subscribeBalance(); return () => { if (unsubscribeHez) unsubscribeHez(); if (unsubscribePez) unsubscribePez(); if (unsubscribeUsdt) unsubscribeUsdt(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [api, assetHubApi, peopleApi, isApiReady, isAssetHubReady, isPeopleReady, selectedAccount]); if (!selectedAccount) { return (

{t('balance.connectWallet')}

); } return (
{/* HEZ Balance Card - Multi-Chain */}
HEZ
{t('balance.hezBalance')}
{t('balance.multiChain')}
{/* Total HEZ */}
{isLoading ? '...' : (parseFloat(balance.total) + parseFloat(assetHubHezBalance) + parseFloat(peopleHezBalance)).toFixed(4)} HEZ
{hezUsdPrice > 0 ? t('balance.usdTotal', { amount: ((parseFloat(balance.total) + parseFloat(assetHubHezBalance) + parseFloat(peopleHezBalance)) * hezUsdPrice).toFixed(2) }) : t('balance.priceLoading')}
{/* Chain Balances */}
{/* Relay Chain (Main) */}
{t('balance.relayChain')}
{balance.free} HEZ
{t('balance.reserved', { amount: balance.reserved })}
{/* Asset Hub */}
{t('balance.assetHub')} {t('balance.pezFees')}
{assetHubHezBalance} HEZ
{parseFloat(assetHubHezBalance) < 0.1 && (
{`⚠️ ${t('balance.lowForFees')}`}
)}
{/* People Chain */}
{t('balance.peopleChain')} {t('balance.identityFees')}
{peopleHezBalance} HEZ
{parseFloat(peopleHezBalance) < 0.1 && (
{`⚠️ ${t('balance.lowForFees')}`}
)}
{/* PEZ Balance Card */}
PEZ {t('balance.pezBalance')}
{isLoading ? '...' : pezBalance} PEZ
{pezUsdPrice > 0 ? `≈ $${(parseFloat(pezBalance) * pezUsdPrice).toFixed(2)} USD` : t('balance.priceLoading')}
{t('balance.govRewardsToken')}
{/* USDT Balance Card */}
USDT {t('balance.usdtBalance')}
{isLoading ? '...' : usdtBalance} USDT
{t('balance.stablecoinOnAssetHub', { amount: usdtBalance })}
{/* wHEZ Balance Card */} {parseFloat(whezBalance) > 0 && (
wHEZ {t('balance.whezBalance')}
{isLoading ? '...' : whezBalance} wHEZ
{hezUsdPrice > 0 ? `≈ $${(parseFloat(whezBalance) * hezUsdPrice).toFixed(2)} USD` : t('balance.priceLoading')} • {t('balance.wrappedHezOnAssetHub')}
)} {/* LP Token Cards */} {lpTokens.length > 0 && (
LP {t('balance.lpTokenPositions')}
{lpTokens.map((lp) => (
{lp.symbol}
{lp.symbol}
{lp.name}
{lp.balance}
{t('balance.poolShare')}
))}
)} {/* Account Info & Scores */} {t('balance.accountInfo')}
{/* Account Details */}
{t('balance.account')} {selectedAccount.meta.name || 'Unnamed'}
{t('balance.address')} {selectedAccount.address.slice(0, 8)}...{selectedAccount.address.slice(-8)}
{/* Scores from Blockchain */}
{t('balance.scoresFromBlockchain')}
{loadingScores ? (
{t('balance.loadingScores')}
) : (
{/* Score Grid */}
{t('balance.trust')}
{scores.trustScore}
{t('balance.referral')}
{scores.referralScore}
{t('balance.staking')}
{scores.stakingScore}
{t('balance.tiki')}
{scores.tikiScore}
{/* Total Score */}
{t('balance.totalScore')} {scores.totalScore}
)}
{/* Other Tokens */}
{t('balance.otherAssets')}
{otherTokens.length === 0 ? (

{t('balance.noCustomTokens')}

{t('balance.addCustomTokensDesc')}

) : (
{otherTokens.map((token) => { const tokenColor = getTokenColor(token.assetId); return (
{/* Token Logo */} {getTokenLogo(token.symbol) ? ( {token.symbol} ) : (
{token.symbol.slice(0, 2).toUpperCase()}
)} {/* Token Info */}
{token.symbol} #{token.assetId}
{token.name}
{/* Balance & Actions */}
{parseFloat(token.balance).toFixed(4)}
${token.usdValue.toFixed(2)} USD
{/* Send Button */}
); })}
)}
{/* Add Token Modal */} setIsAddTokenModalOpen(false)} onAddToken={handleAddToken} /> {/* Transfer Modal */} { setIsTransferModalOpen(false); setSelectedTokenForTransfer(null); }} selectedAsset={selectedTokenForTransfer} /> {/* XCM Teleport Modal */} setIsXCMTeleportModalOpen(false)} /> {/* LP Stake Modal */} { setIsLPStakeModalOpen(false); setSelectedLPForStake(null); }} lpToken={selectedLPForStake} onStakeSuccess={() => fetchBalance()} />
); };