diff --git a/web/src/components/AccountBalance.tsx b/web/src/components/AccountBalance.tsx index a0236571..044a5c4c 100644 --- a/web/src/components/AccountBalance.tsx +++ b/web/src/components/AccountBalance.tsx @@ -18,7 +18,7 @@ interface TokenBalance { } export const AccountBalance: React.FC = () => { - const { api, isApiReady, selectedAccount } = usePezkuwi(); + const { api, assetHubApi, isApiReady, isAssetHubReady, selectedAccount } = usePezkuwi(); const [balance, setBalance] = useState<{ free: string; reserved: string; @@ -318,21 +318,26 @@ export const AccountBalance: React.FC = () => { total: totalTokens, }); - // Fetch PEZ balance (Asset ID: 1) + // Fetch PEZ balance (Asset ID: 1) from Asset Hub try { - const pezAssetBalance = await api.query.assets.account(1, selectedAccount.address); + 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); + 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 { - setPezBalance('0'); + 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:', error); - setPezBalance('0'); + 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) @@ -460,26 +465,28 @@ export const AccountBalance: React.FC = () => { } ); - // Subscribe to PEZ balance (Asset ID: 1) - try { - unsubscribePez = await api.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'); + // 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:', error); + ); + } 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: 2) @@ -513,7 +520,7 @@ export const AccountBalance: React.FC = () => { if (unsubscribeUsdt) unsubscribeUsdt(); }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [api, isApiReady, selectedAccount]); + }, [api, assetHubApi, isApiReady, isAssetHubReady, selectedAccount]); if (!selectedAccount) { return ( diff --git a/web/src/contexts/PezkuwiContext.tsx b/web/src/contexts/PezkuwiContext.tsx index a20cdbbd..7c4f6ddb 100644 --- a/web/src/contexts/PezkuwiContext.tsx +++ b/web/src/contexts/PezkuwiContext.tsx @@ -5,9 +5,14 @@ import type { InjectedAccountWithMeta } from '@pezkuwi/extension-inject/types'; import { DEFAULT_ENDPOINT } from '../../../shared/blockchain/pezkuwi'; import { isMobileApp, getNativeWalletAddress, getNativeAccountName } from '@/lib/mobile-bridge'; +// Asset Hub endpoint for PEZ token queries +const ASSET_HUB_ENDPOINT = 'wss://asset-hub-rpc.pezkuwichain.io'; + interface PezkuwiContextType { api: ApiPromise | null; + assetHubApi: ApiPromise | null; isApiReady: boolean; + isAssetHubReady: boolean; isConnected: boolean; accounts: InjectedAccountWithMeta[]; selectedAccount: InjectedAccountWithMeta | null; @@ -30,7 +35,9 @@ export const PezkuwiProvider: React.FC = ({ endpoint = DEFAULT_ENDPOINT // Beta testnet RPC from shared config }) => { const [api, setApi] = useState(null); + const [assetHubApi, setAssetHubApi] = useState(null); const [isApiReady, setIsApiReady] = useState(false); + const [isAssetHubReady, setIsAssetHubReady] = useState(false); const [accounts, setAccounts] = useState([]); const [selectedAccount, setSelectedAccount] = useState(null); const [error, setError] = useState(null); @@ -127,12 +134,50 @@ export const PezkuwiProvider: React.FC = ({ setIsApiReady(false); }; + // Initialize Asset Hub API for PEZ token + const initAssetHubApi = async () => { + try { + if (import.meta.env.DEV) { + console.log('🔗 Connecting to Asset Hub:', ASSET_HUB_ENDPOINT); + } + + const provider = new WsProvider(ASSET_HUB_ENDPOINT); + const assetHubApiInstance = await ApiPromise.create({ + provider, + signedExtensions: { + AuthorizeCall: { + extrinsic: {}, + payload: {}, + }, + }, + }); + + await assetHubApiInstance.isReady; + + setAssetHubApi(assetHubApiInstance); + setIsAssetHubReady(true); + + if (import.meta.env.DEV) { + console.log('✅ Connected to Asset Hub for PEZ token'); + } + } catch (err) { + if (import.meta.env.DEV) { + console.error('❌ Failed to connect to Asset Hub:', err); + } + // Don't set error - PEZ features just won't work + } + }; + initApi(); + initAssetHubApi(); return () => { if (api) { api.disconnect(); } + if (assetHubApi) { + assetHubApi.disconnect(); + } }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [endpoint]); @@ -311,7 +356,9 @@ export const PezkuwiProvider: React.FC = ({ const value: PezkuwiContextType = { api, + assetHubApi, isApiReady, + isAssetHubReady, isConnected: isApiReady, // Alias for backward compatibility accounts, selectedAccount, diff --git a/web/src/contexts/WebSocketContext.tsx b/web/src/contexts/WebSocketContext.tsx index 2daa8954..2c5791f9 100644 --- a/web/src/contexts/WebSocketContext.tsx +++ b/web/src/contexts/WebSocketContext.tsx @@ -17,12 +17,20 @@ interface WebSocketContextType { const WebSocketContext = createContext(null); -const ENDPOINTS = [ - 'ws://localhost:8082', // Local Vite dev server - 'ws://127.0.0.1:9944', // Local development node (primary) - 'ws://localhost:9944', // Local development node (alternative) - 'wss://ws.pezkuwichain.io', // Production WebSocket (fallback) -]; +// Only use localhost endpoints if running locally +const isLocalhost = typeof window !== 'undefined' && + (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'); + +const ENDPOINTS = isLocalhost + ? [ + 'ws://localhost:8082', // Local Vite dev server + 'ws://127.0.0.1:9944', // Local development node (primary) + 'ws://localhost:9944', // Local development node (alternative) + 'wss://ws.pezkuwichain.io', // Production WebSocket (fallback) + ] + : [ + 'wss://ws.pezkuwichain.io', // Production WebSocket only + ]; export const useWebSocket = () => { const context = useContext(WebSocketContext);