From 34486018bb00d18c835081742e3e33f9b6cea33d Mon Sep 17 00:00:00 2001 From: Kurdistan Tech Ministry Date: Wed, 4 Feb 2026 13:13:24 +0300 Subject: [PATCH] feat: migrate DEX modals to Asset Hub API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEX pallets (tokenWrapper, assets, assetConversion) are deployed on Asset Hub teyrchain, not on the relay chain. Updated all DEX modals to use assetHubApi instead of relay chain api: - InitializeHezPoolModal: HEZ → wHEZ wrapping via Asset Hub - InitializeUsdtModal: wUSDT minting via Asset Hub - CreatePoolModal: Pool creation via Asset Hub - XCMConfigurationWizard: Steps 5-6 use Asset Hub API Added pallet availability checks with user-friendly error messages. --- .../admin/XCMConfigurationWizard.tsx | 30 +++++-- web/src/components/dex/CreatePoolModal.tsx | 43 +++++---- .../components/dex/InitializeHezPoolModal.tsx | 87 +++++++++++++++---- .../components/dex/InitializeUsdtModal.tsx | 36 +++++--- 4 files changed, 142 insertions(+), 54 deletions(-) diff --git a/web/src/components/admin/XCMConfigurationWizard.tsx b/web/src/components/admin/XCMConfigurationWizard.tsx index 839148d9..4548709f 100644 --- a/web/src/components/admin/XCMConfigurationWizard.tsx +++ b/web/src/components/admin/XCMConfigurationWizard.tsx @@ -65,7 +65,9 @@ export const XCMConfigurationWizard: React.FC = ({ onClose, onSuccess, }) => { - const { api, isApiReady } = usePezkuwi(); + // Use Asset Hub API for asset registration (Step 5) and XCM testing (Step 6) + // Steps 1-4 connect to relay chain directly via xcm-wizard functions + const { assetHubApi, isAssetHubReady } = usePezkuwi(); const { account, signer } = useWallet(); const { toast } = useToast(); @@ -324,10 +326,17 @@ export const XCMConfigurationWizard: React.FC = ({ }; // ======================================== - // STEP 5: REGISTER FOREIGN ASSETS + // STEP 5: REGISTER FOREIGN ASSETS (on Asset Hub) // ======================================== const handleRegisterAssets = async () => { - if (!api || !isApiReady || !account || !signer) return; + if (!assetHubApi || !isAssetHubReady || !account || !signer) { + toast({ + title: 'Not Ready', + description: 'Please wait for Asset Hub connection', + variant: 'destructive', + }); + return; + } setRegisteringAssets(true); try { @@ -363,7 +372,7 @@ export const XCMConfigurationWizard: React.FC = ({ }, ]; - const registered = await registerForeignAssets(api, foreignAssets, account); + const registered = await registerForeignAssets(assetHubApi, foreignAssets, account); setRegisteredAssets(registered); setSteps(prev => ({ @@ -394,14 +403,21 @@ export const XCMConfigurationWizard: React.FC = ({ }; // ======================================== - // STEP 6: TEST XCM TRANSFER + // STEP 6: TEST XCM TRANSFER (on Asset Hub) // ======================================== const handleTestXCMTransfer = async () => { - if (!api || !isApiReady || !account || !signer) return; + if (!assetHubApi || !isAssetHubReady || !account || !signer) { + toast({ + title: 'Not Ready', + description: 'Please wait for Asset Hub connection', + variant: 'destructive', + }); + return; + } setTesting(true); try { - const result = await testXCMTransfer(api, '1000000', account); // 1 USDT (6 decimals) + const result = await testXCMTransfer(assetHubApi, '1000000', account); // 1 USDT (6 decimals) setTestResult(result); diff --git a/web/src/components/dex/CreatePoolModal.tsx b/web/src/components/dex/CreatePoolModal.tsx index 36c82ad1..52adb7c7 100644 --- a/web/src/components/dex/CreatePoolModal.tsx +++ b/web/src/components/dex/CreatePoolModal.tsx @@ -20,7 +20,8 @@ export const CreatePoolModal: React.FC = ({ onClose, onSuccess, }) => { - const { api, isApiReady } = usePezkuwi(); + // Use Asset Hub API for DEX operations (assetConversion pallet is on Asset Hub) + const { assetHubApi, isAssetHubReady } = usePezkuwi(); const { account, signer } = useWallet(); const [asset1Id, setAsset1Id] = useState(null); @@ -49,14 +50,14 @@ export const CreatePoolModal: React.FC = ({ } }, [isOpen]); - // Fetch balances when assets selected + // Fetch balances from Asset Hub when assets selected useEffect(() => { const fetchBalances = async () => { - if (!api || !isApiReady || !account || asset1Id === null) return; + if (!assetHubApi || !isAssetHubReady || !account || asset1Id === null) return; try { - if (import.meta.env.DEV) console.log('🔍 Fetching balance for asset', asset1Id, 'account', account); - const balance1Data = await api.query.assets.account(asset1Id, account); + if (import.meta.env.DEV) console.log('🔍 Fetching balance for asset', asset1Id, 'on Asset Hub'); + const balance1Data = await assetHubApi.query.assets.account(asset1Id, account); if (balance1Data.isSome) { const balance = balance1Data.unwrap().balance.toString(); if (import.meta.env.DEV) console.log('✅ Balance found for asset', asset1Id, ':', balance); @@ -72,15 +73,15 @@ export const CreatePoolModal: React.FC = ({ }; fetchBalances(); - }, [api, isApiReady, account, asset1Id]); + }, [assetHubApi, isAssetHubReady, account, asset1Id]); useEffect(() => { const fetchBalances = async () => { - if (!api || !isApiReady || !account || asset2Id === null) return; + if (!assetHubApi || !isAssetHubReady || !account || asset2Id === null) return; try { - if (import.meta.env.DEV) console.log('🔍 Fetching balance for asset', asset2Id, 'account', account); - const balance2Data = await api.query.assets.account(asset2Id, account); + if (import.meta.env.DEV) console.log('🔍 Fetching balance for asset', asset2Id, 'on Asset Hub'); + const balance2Data = await assetHubApi.query.assets.account(asset2Id, account); if (balance2Data.isSome) { const balance = balance2Data.unwrap().balance.toString(); if (import.meta.env.DEV) console.log('✅ Balance found for asset', asset2Id, ':', balance); @@ -96,7 +97,7 @@ export const CreatePoolModal: React.FC = ({ }; fetchBalances(); - }, [api, isApiReady, account, asset2Id]); + }, [assetHubApi, isAssetHubReady, account, asset2Id]); const validateInputs = (): string | null => { if (asset1Id === null || asset2Id === null) { @@ -150,8 +151,14 @@ export const CreatePoolModal: React.FC = ({ }; const handleCreatePool = async () => { - if (!api || !isApiReady || !signer || !account) { - setErrorMessage('Wallet not connected'); + if (!assetHubApi || !isAssetHubReady || !signer || !account) { + setErrorMessage('Wallet not connected or Asset Hub not ready'); + return; + } + + // Check if assetConversion pallet is available on Asset Hub + if (!assetHubApi.tx.assetConversion || !assetHubApi.tx.assetConversion.createPool) { + setErrorMessage('AssetConversion pallet is not available on Asset Hub. Pool creation requires this pallet.'); return; } @@ -170,11 +177,11 @@ export const CreatePoolModal: React.FC = ({ setTxStatus('signing'); setErrorMessage(''); - // Create pool extrinsic - const createPoolTx = api.tx.assetConversion.createPool(asset1Id, asset2Id); + // Create pool extrinsic on Asset Hub + const createPoolTx = assetHubApi.tx.assetConversion.createPool(asset1Id, asset2Id); - // Add liquidity extrinsic - const addLiquidityTx = api.tx.assetConversion.addLiquidity( + // Add liquidity extrinsic on Asset Hub + const addLiquidityTx = assetHubApi.tx.assetConversion.addLiquidity( asset1Id, asset2Id, amount1Raw, @@ -185,7 +192,7 @@ export const CreatePoolModal: React.FC = ({ ); // Batch transactions - const batchTx = api.tx.utility.batchAll([createPoolTx, addLiquidityTx]); + const batchTx = assetHubApi.tx.utility.batchAll([createPoolTx, addLiquidityTx]); setTxStatus('submitting'); @@ -196,7 +203,7 @@ export const CreatePoolModal: React.FC = ({ if (status.isInBlock) { if (dispatchError) { if (dispatchError.isModule) { - const decoded = api.registry.findMetaError(dispatchError.asModule); + const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule); setErrorMessage(`${decoded.section}.${decoded.name}: ${decoded.docs}`); } else { setErrorMessage(dispatchError.toString()); diff --git a/web/src/components/dex/InitializeHezPoolModal.tsx b/web/src/components/dex/InitializeHezPoolModal.tsx index 6a4124ce..73e73f62 100644 --- a/web/src/components/dex/InitializeHezPoolModal.tsx +++ b/web/src/components/dex/InitializeHezPoolModal.tsx @@ -22,7 +22,8 @@ export const InitializeHezPoolModal: React.FC = ({ onClose, onSuccess, }) => { - const { api, isApiReady } = usePezkuwi(); + // Use Asset Hub API for DEX operations (tokenWrapper pallet is on Asset Hub) + const { assetHubApi, isAssetHubReady } = usePezkuwi(); const { account, signer } = useWallet(); const { toast } = useToast(); @@ -30,6 +31,7 @@ export const InitializeHezPoolModal: React.FC = ({ const [hezBalance, setHezBalance] = useState('0'); const [whezBalance, setWhezBalance] = useState('0'); + const [palletAvailable, setPalletAvailable] = useState(null); const [txStatus, setTxStatus] = useState('idle'); const [errorMessage, setErrorMessage] = useState(''); @@ -43,33 +45,72 @@ export const InitializeHezPoolModal: React.FC = ({ } }, [isOpen]); - // Fetch balances + // Check if tokenWrapper pallet is available on Asset Hub useEffect(() => { - const fetchBalances = async () => { - if (!api || !isApiReady || !account) return; + const checkPallet = async () => { + if (!assetHubApi || !isAssetHubReady) { + setPalletAvailable(null); + return; + } try { - // HEZ balance (native) - const balance = await api.query.system.account(account); + // Check if tokenWrapper pallet exists on Asset Hub + const hasTokenWrapper = assetHubApi.tx.tokenWrapper !== undefined && + assetHubApi.tx.tokenWrapper.wrap !== 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); + setPalletAvailable(false); + } + }; + + checkPallet(); + }, [assetHubApi, isAssetHubReady]); + + // Fetch balances from Asset Hub + useEffect(() => { + const fetchBalances = async () => { + if (!assetHubApi || !isAssetHubReady || !account) return; + + try { + // HEZ balance (native on Asset Hub) + const balance = await assetHubApi.query.system.account(account); const freeBalance = balance.data.free.toString(); setHezBalance(freeBalance); - // wHEZ balance (asset 0) - const whezData = await api.query.assets.account(0, account); + // wHEZ balance (asset 0 on Asset Hub) + const whezData = await assetHubApi.query.assets.account(0, account); setWhezBalance(whezData.isSome ? whezData.unwrap().balance.toString() : '0'); } catch (error) { - if (import.meta.env.DEV) console.error('Failed to fetch balances:', error); + if (import.meta.env.DEV) console.error('Failed to fetch balances from Asset Hub:', error); } }; fetchBalances(); - }, [api, isApiReady, account]); + }, [assetHubApi, isAssetHubReady, account]); const handleWrap = async () => { - if (!api || !isApiReady || !signer || !account) { + if (!assetHubApi || !isAssetHubReady || !signer || !account) { toast({ title: 'Error', - description: 'Please connect your wallet', + description: 'Please connect your wallet and wait for Asset Hub connection', + variant: 'destructive', + }); + return; + } + + if (!palletAvailable) { + setErrorMessage('TokenWrapper pallet is not available on Asset Hub. Please contact the team.'); + toast({ + title: 'Pallet Not Available', + description: 'The TokenWrapper pallet is not deployed on Asset Hub.', variant: 'destructive', }); return; @@ -83,7 +124,7 @@ export const InitializeHezPoolModal: React.FC = ({ } if (hezAmountRaw > BigInt(hezBalance)) { - setErrorMessage('Insufficient HEZ balance'); + setErrorMessage('Insufficient HEZ balance on Asset Hub'); return; } @@ -91,12 +132,12 @@ export const InitializeHezPoolModal: React.FC = ({ setErrorMessage(''); try { - if (import.meta.env.DEV) console.log('🔄 Wrapping HEZ to wHEZ...', { + if (import.meta.env.DEV) console.log('🔄 Wrapping HEZ to wHEZ on Asset Hub...', { hezAmount, hezAmountRaw: hezAmountRaw.toString(), }); - const wrapTx = api.tx.tokenWrapper.wrap(hezAmountRaw.toString()); + const wrapTx = assetHubApi.tx.tokenWrapper.wrap(hezAmountRaw.toString()); setTxStatus('submitting'); @@ -113,7 +154,7 @@ export const InitializeHezPoolModal: React.FC = ({ let errorMsg = ''; if (dispatchError.isModule) { - const decoded = api.registry.findMetaError(dispatchError.asModule); + const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule); errorMsg = `${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`; if (import.meta.env.DEV) console.error('❌ Module error:', errorMsg); } else { @@ -183,6 +224,17 @@ export const InitializeHezPoolModal: React.FC = ({ + {/* Pallet Availability Warning */} + {palletAvailable === false && ( + + + + TokenWrapper pallet not deployed. This feature requires the TokenWrapper pallet to be deployed on the blockchain runtime. + Please contact the development team. + + + )} + {/* Info Banner */} @@ -266,7 +318,8 @@ export const InitializeHezPoolModal: React.FC = ({ disabled={ txStatus === 'signing' || txStatus === 'submitting' || - txStatus === 'success' + txStatus === 'success' || + palletAvailable === false } > {txStatus === 'signing' && ( diff --git a/web/src/components/dex/InitializeUsdtModal.tsx b/web/src/components/dex/InitializeUsdtModal.tsx index db3172c5..afb03b2f 100644 --- a/web/src/components/dex/InitializeUsdtModal.tsx +++ b/web/src/components/dex/InitializeUsdtModal.tsx @@ -25,7 +25,8 @@ export const InitializeUsdtModal: React.FC = ({ onClose, onSuccess, }) => { - const { api, isApiReady } = usePezkuwi(); + // Use Asset Hub API for DEX operations (assets pallet is on Asset Hub) + const { assetHubApi, isAssetHubReady } = usePezkuwi(); const { account, signer } = useWallet(); const { toast } = useToast(); @@ -45,28 +46,39 @@ export const InitializeUsdtModal: React.FC = ({ } }, [isOpen]); - // Fetch wUSDT balance + // Fetch wUSDT balance from Asset Hub useEffect(() => { const fetchBalance = async () => { - if (!api || !isApiReady || !account) return; + if (!assetHubApi || !isAssetHubReady || !account) return; try { - // wUSDT balance (asset 2) - const wusdtData = await api.query.assets.account(USDT_ASSET_ID, account); + // wUSDT balance (asset 1000 on Asset Hub) + const wusdtData = await assetHubApi.query.assets.account(USDT_ASSET_ID, account); setWusdtBalance(wusdtData.isSome ? wusdtData.unwrap().balance.toString() : '0'); } catch (error) { - if (import.meta.env.DEV) console.error('Failed to fetch wUSDT balance:', error); + if (import.meta.env.DEV) console.error('Failed to fetch wUSDT balance from Asset Hub:', error); } }; fetchBalance(); - }, [api, isApiReady, account]); + }, [assetHubApi, isAssetHubReady, account]); const handleMint = async () => { - if (!api || !isApiReady || !signer || !account) { + if (!assetHubApi || !isAssetHubReady || !signer || !account) { toast({ title: 'Error', - description: 'Please connect your wallet', + description: 'Please connect your wallet and wait for Asset Hub connection', + variant: 'destructive', + }); + return; + } + + // Check if assets pallet is available on Asset Hub + if (!assetHubApi.tx.assets || !assetHubApi.tx.assets.mint) { + setErrorMessage('Assets pallet is not available on Asset Hub.'); + toast({ + title: 'Pallet Not Available', + description: 'The Assets pallet is not deployed on Asset Hub.', variant: 'destructive', }); return; @@ -83,13 +95,13 @@ export const InitializeUsdtModal: React.FC = ({ setErrorMessage(''); try { - if (import.meta.env.DEV) console.log('💵 Minting wUSDT...', { + if (import.meta.env.DEV) console.log('💵 Minting wUSDT on Asset Hub...', { usdtAmount, usdtAmountRaw: usdtAmountRaw.toString(), assetId: USDT_ASSET_ID, }); - const mintTx = api.tx.assets.mint(USDT_ASSET_ID, account, usdtAmountRaw.toString()); + const mintTx = assetHubApi.tx.assets.mint(USDT_ASSET_ID, account, usdtAmountRaw.toString()); setTxStatus('submitting'); @@ -106,7 +118,7 @@ export const InitializeUsdtModal: React.FC = ({ let errorMsg = ''; if (dispatchError.isModule) { - const decoded = api.registry.findMetaError(dispatchError.asModule); + const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule); errorMsg = `${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`; if (import.meta.env.DEV) console.error('❌ Module error:', errorMsg); } else {