From cd6da00ef82382fce394806893009f9f304bc146 Mon Sep 17 00:00:00 2001 From: Kurdistan Tech Ministry Date: Wed, 4 Feb 2026 23:27:36 +0300 Subject: [PATCH] fix: DEX improvements - Asset Hub balance fetching and Treasury admin support - WalletContext: Use Asset Hub API for PEZ/wHEZ/wUSDT balance queries - AddLiquidityModal: Handle native token balance via system.account - auth.ts: Add Treasury accounts to admin list for pool creation and minting Co-Authored-By: Claude Opus 4.5 --- shared/utils/auth.ts | 23 +++++++++++--- web/src/components/dex/AddLiquidityModal.tsx | 20 +++++++++--- web/src/contexts/WalletContext.tsx | 32 ++++++++++++++------ 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/shared/utils/auth.ts b/shared/utils/auth.ts index 13ced150..681eb3ef 100644 --- a/shared/utils/auth.ts +++ b/shared/utils/auth.ts @@ -8,11 +8,20 @@ export const FOUNDER_ADDRESS_FALLBACK = '5GgTgG9sRmPQAYU1RsTejZYnZRjwzKZKWD3awtuqjHioki45'; /** - * Check if given address is the sudo account (admin/founder) - * SECURITY: Only allows admin access when connected to blockchain with valid sudo key + * Treasury and admin accounts that have elevated permissions + * These accounts can perform admin operations like creating pools, minting tokens, etc. + */ +export const TREASURY_ADDRESSES = [ + '5EhCpn82QtdU53MF6PoNFrKHgSrsfcAxFTMwrn3JYf9dioQw', // Treasury 1 - wUSDT issuer/admin +] as const; + +/** + * Check if given address is the sudo account (admin/founder) or a treasury account + * SECURITY: Only allows admin access when connected to blockchain with valid sudo key, + * or if the address is a known treasury account * @param address - Substrate address to check * @param sudoKey - Sudo key fetched from blockchain (REQUIRED for admin access) - * @returns true if address matches sudo key from blockchain + * @returns true if address matches sudo key from blockchain or is a treasury account */ export const isFounderWallet = ( address: string | null | undefined, @@ -20,6 +29,11 @@ export const isFounderWallet = ( ): boolean => { if (!address) return false; + // Check if address is a known treasury account + if (TREASURY_ADDRESSES.includes(address as typeof TREASURY_ADDRESSES[number])) { + return true; + } + // SECURITY FIX: ONLY use dynamic sudo key from blockchain // No fallback to hardcoded address - admin access requires active blockchain connection if (!sudoKey || sudoKey === '') { @@ -99,6 +113,7 @@ export const getUserRole = ( sudoKey?: string | null ): string => { if (!address) return 'Guest'; - if (isFounderWallet(address, sudoKey)) return 'Sudo (Admin)'; + if (address === sudoKey) return 'Sudo (Admin)'; + if (TREASURY_ADDRESSES.includes(address as typeof TREASURY_ADDRESSES[number])) return 'Treasury (Admin)'; return 'User'; }; diff --git a/web/src/components/dex/AddLiquidityModal.tsx b/web/src/components/dex/AddLiquidityModal.tsx index 54b8faa9..bb5e9b07 100644 --- a/web/src/components/dex/AddLiquidityModal.tsx +++ b/web/src/components/dex/AddLiquidityModal.tsx @@ -61,11 +61,23 @@ export const AddLiquidityModal: React.FC = ({ if (!assetHubApi || !isAssetHubReady || !account || !pool) return; try { - const balance1Data = await assetHubApi.query.assets.account(pool.asset1, account); - const balance2Data = await assetHubApi.query.assets.account(pool.asset2, account); + // Fetch balance for asset1 - handle native token differently + if (pool.asset1 === NATIVE_TOKEN_ID) { + const accountInfo = await assetHubApi.query.system.account(account); + setBalance1(accountInfo.data.free.toString()); + } else { + const balance1Data = await assetHubApi.query.assets.account(pool.asset1, account); + setBalance1(balance1Data.isSome ? balance1Data.unwrap().balance.toString() : '0'); + } - setBalance1(balance1Data.isSome ? balance1Data.unwrap().balance.toString() : '0'); - setBalance2(balance2Data.isSome ? balance2Data.unwrap().balance.toString() : '0'); + // Fetch balance for asset2 - handle native token differently + if (pool.asset2 === NATIVE_TOKEN_ID) { + const accountInfo = await assetHubApi.query.system.account(account); + setBalance2(accountInfo.data.free.toString()); + } else { + const balance2Data = await assetHubApi.query.assets.account(pool.asset2, account); + setBalance2(balance2Data.isSome ? balance2Data.unwrap().balance.toString() : '0'); + } } catch (error) { if (import.meta.env.DEV) console.error('Failed to fetch balances:', error); } diff --git a/web/src/contexts/WalletContext.tsx b/web/src/contexts/WalletContext.tsx index 76f117a7..64d63fed 100644 --- a/web/src/contexts/WalletContext.tsx +++ b/web/src/contexts/WalletContext.tsx @@ -53,6 +53,7 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr const [signer, setSigner] = useState(null); // Fetch all token balances when account changes + // Uses relay chain API for native HEZ, Asset Hub API for PEZ/wHEZ/wUSDT const updateBalance = useCallback(async (address: string) => { if (!pezkuwi.api || !pezkuwi.isApiReady) { if (import.meta.env.DEV) console.warn('API not ready, cannot fetch balance'); @@ -62,15 +63,25 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr try { if (import.meta.env.DEV) console.log('💰 Fetching all token balances for:', address); - // Fetch HEZ (native token) + // Fetch HEZ (native token) from relay chain const { data: nativeBalance } = await pezkuwi.api.query.system.account(address); const hezBalance = formatBalance(nativeBalance.free.toString()); setBalance(hezBalance); // Legacy support - // Fetch PEZ (Asset ID: 1) + // For assets (PEZ, wHEZ, wUSDT), use Asset Hub API since they're on Asset Hub + // Fall back to relay chain API if Asset Hub not ready (some chains have assets on relay) + const assetApi = pezkuwi.assetHubApi && pezkuwi.isAssetHubReady + ? pezkuwi.assetHubApi + : pezkuwi.api; + + if (import.meta.env.DEV) { + console.log('📊 Using API for assets:', pezkuwi.assetHubApi && pezkuwi.isAssetHubReady ? 'Asset Hub' : 'Relay Chain'); + } + + // Fetch PEZ (Asset ID: 1) from Asset Hub let pezBalance = '0'; try { - const pezData = await pezkuwi.api.query.assets.account(ASSET_IDS.PEZ, address); + const pezData = await assetApi.query.assets.account(ASSET_IDS.PEZ, address); if (import.meta.env.DEV) console.log('📊 Raw PEZ data:', pezData.toHuman()); if (pezData.isSome) { @@ -85,10 +96,10 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr if (import.meta.env.DEV) console.error('❌ Failed to fetch PEZ balance:', err); } - // Fetch wHEZ (Asset ID: 0) + // Fetch wHEZ (Asset ID: 2) from Asset Hub let whezBalance = '0'; try { - const whezData = await pezkuwi.api.query.assets.account(ASSET_IDS.WHEZ, address); + const whezData = await assetApi.query.assets.account(ASSET_IDS.WHEZ, address); if (import.meta.env.DEV) console.log('📊 Raw wHEZ data:', whezData.toHuman()); if (whezData.isSome) { @@ -103,10 +114,10 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr if (import.meta.env.DEV) console.error('❌ Failed to fetch wHEZ balance:', err); } - // Fetch wUSDT (Asset ID: 2) - IMPORTANT: wUSDT has 6 decimals, not 12! + // Fetch wUSDT (Asset ID: 1000) from Asset Hub - IMPORTANT: wUSDT has 6 decimals, not 12! let wusdtBalance = '0'; try { - const wusdtData = await pezkuwi.api.query.assets.account(ASSET_IDS.WUSDT, address); + const wusdtData = await assetApi.query.assets.account(ASSET_IDS.WUSDT, address); if (import.meta.env.DEV) console.log('📊 Raw wUSDT data:', wusdtData.toHuman()); if (wusdtData.isSome) { @@ -133,7 +144,7 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr if (import.meta.env.DEV) console.error('Failed to fetch balances:', err); setError('Failed to fetch balances'); } - }, [pezkuwi.api, pezkuwi.isApiReady]); + }, [pezkuwi.api, pezkuwi.isApiReady, pezkuwi.assetHubApi, pezkuwi.isAssetHubReady]); // Connect wallet (Pezkuwi.js extension) const connectWallet = useCallback(async () => { @@ -268,18 +279,19 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr getSigner(); }, [pezkuwi.selectedAccount]); - // Update balance when selected account changes + // Update balance when selected account changes or Asset Hub becomes ready useEffect(() => { if (import.meta.env.DEV) console.log('🔄 WalletContext useEffect triggered!', { hasAccount: !!pezkuwi.selectedAccount, isApiReady: pezkuwi.isApiReady, + isAssetHubReady: pezkuwi.isAssetHubReady, address: pezkuwi.selectedAccount?.address }); if (pezkuwi.selectedAccount && pezkuwi.isApiReady) { updateBalance(pezkuwi.selectedAccount.address); } - }, [pezkuwi.selectedAccount, pezkuwi.isApiReady, updateBalance]); + }, [pezkuwi.selectedAccount, pezkuwi.isApiReady, pezkuwi.isAssetHubReady, updateBalance]); // Sync error state with PezkuwiContext useEffect(() => {