mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-22 04:27:56 +00:00
fix: migrate DEX components from Relay Chain to Asset Hub API
- Update PoolDashboard to use assetHubApi for pool discovery - Update TokenSwap to use assetHubApi for swap operations - Update AddLiquidityModal to use assetHubApi - Update RemoveLiquidityModal (both versions) to use assetHubApi - Use XCM Location format for pool queries (Native HEZ support) - Fix all lint errors and dependency array warnings
This commit is contained in:
@@ -54,7 +54,8 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
asset0 = 0, // Default to wHEZ
|
||||
asset1 = 1 // Default to PEZ
|
||||
}) => {
|
||||
const { api, selectedAccount, isApiReady } = usePezkuwi();
|
||||
// Use Asset Hub API for DEX operations (assetConversion pallet is on Asset Hub)
|
||||
const { assetHubApi, selectedAccount, isAssetHubReady } = usePezkuwi();
|
||||
const { balances, refreshBalances } = useWallet();
|
||||
|
||||
const [amount0, setAmount0] = useState('');
|
||||
@@ -87,13 +88,13 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
|
||||
// Fetch minimum deposit requirements from runtime
|
||||
useEffect(() => {
|
||||
if (!api || !isApiReady || !isOpen) return;
|
||||
if (!assetHubApi || !isAssetHubReady || !isOpen) return;
|
||||
|
||||
const fetchMinimumBalances = async () => {
|
||||
try {
|
||||
// Query asset details which contains minBalance
|
||||
const assetDetails0 = await api.query.assets.asset(asset0);
|
||||
const assetDetails1 = await api.query.assets.asset(asset1);
|
||||
const assetDetails0 = await assetHubApi.query.assets.asset(asset0);
|
||||
const assetDetails1 = await assetHubApi.query.assets.asset(asset1);
|
||||
|
||||
if (import.meta.env.DEV) console.log('🔍 Querying minimum balances for assets:', { asset0, asset1 });
|
||||
|
||||
@@ -128,19 +129,19 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
}
|
||||
|
||||
// Also check if there's a MintMinLiquidity constant in assetConversion pallet
|
||||
if (api.consts.assetConversion) {
|
||||
const mintMinLiq = api.consts.assetConversion.mintMinLiquidity;
|
||||
if (assetHubApi.consts.assetConversion) {
|
||||
const mintMinLiq = assetHubApi.consts.assetConversion.mintMinLiquidity;
|
||||
if (mintMinLiq) {
|
||||
if (import.meta.env.DEV) console.log('🔧 AssetConversion MintMinLiquidity constant:', mintMinLiq.toString());
|
||||
}
|
||||
|
||||
const liquidityWithdrawalFee = api.consts.assetConversion.liquidityWithdrawalFee;
|
||||
const liquidityWithdrawalFee = assetHubApi.consts.assetConversion.liquidityWithdrawalFee;
|
||||
if (liquidityWithdrawalFee) {
|
||||
if (import.meta.env.DEV) console.log('🔧 AssetConversion LiquidityWithdrawalFee:', liquidityWithdrawalFee.toHuman());
|
||||
}
|
||||
|
||||
// Log all assetConversion constants
|
||||
if (import.meta.env.DEV) console.log('🔧 All assetConversion constants:', Object.keys(api.consts.assetConversion));
|
||||
if (import.meta.env.DEV) console.log('🔧 All assetConversion constants:', Object.keys(assetHubApi.consts.assetConversion));
|
||||
}
|
||||
} catch (err) {
|
||||
if (import.meta.env.DEV) console.error('❌ Error fetching minimum balances:', err);
|
||||
@@ -149,16 +150,16 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
};
|
||||
|
||||
fetchMinimumBalances();
|
||||
}, [api, isApiReady, isOpen, asset0, asset1, asset0Decimals, asset1Decimals, asset0Name, asset1Name]);
|
||||
}, [assetHubApi, isAssetHubReady, isOpen, asset0, asset1, asset0Decimals, asset1Decimals, asset0Name, asset1Name]);
|
||||
|
||||
// Fetch current pool price
|
||||
useEffect(() => {
|
||||
if (!api || !isApiReady || !isOpen) return;
|
||||
if (!assetHubApi || !isAssetHubReady || !isOpen) return;
|
||||
|
||||
const fetchPoolPrice = async () => {
|
||||
try {
|
||||
const poolId = [asset0, asset1];
|
||||
const poolInfo = await api.query.assetConversion.pools(poolId);
|
||||
const poolInfo = await assetHubApi.query.assetConversion.pools(poolId);
|
||||
|
||||
if (poolInfo.isSome) {
|
||||
// Derive pool account using AccountIdConverter
|
||||
@@ -166,16 +167,16 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
const { blake2AsU8a } = await import('@pezkuwi/util-crypto');
|
||||
|
||||
const PALLET_ID = stringToU8a('py/ascon');
|
||||
const poolIdType = api.createType('(u32, u32)', [asset0, asset1]);
|
||||
const palletIdType = api.createType('[u8; 8]', PALLET_ID);
|
||||
const fullTuple = api.createType('([u8; 8], (u32, u32))', [palletIdType, poolIdType]);
|
||||
const poolIdType = assetHubApi.createType('(u32, u32)', [asset0, asset1]);
|
||||
const palletIdType = assetHubApi.createType('[u8; 8]', PALLET_ID);
|
||||
const fullTuple = assetHubApi.createType('([u8; 8], (u32, u32))', [palletIdType, poolIdType]);
|
||||
|
||||
const accountHash = blake2AsU8a(fullTuple.toU8a(), 256);
|
||||
const poolAccountId = api.createType('AccountId32', accountHash);
|
||||
const poolAccountId = assetHubApi.createType('AccountId32', accountHash);
|
||||
|
||||
// Get reserves
|
||||
const balance0Data = await api.query.assets.account(asset0, poolAccountId);
|
||||
const balance1Data = await api.query.assets.account(asset1, poolAccountId);
|
||||
const balance0Data = await assetHubApi.query.assets.account(asset0, poolAccountId);
|
||||
const balance1Data = await assetHubApi.query.assets.account(asset1, poolAccountId);
|
||||
|
||||
if (balance0Data.isSome && balance1Data.isSome) {
|
||||
const data0 = balance0Data.unwrap().toJSON() as AssetAccountData;
|
||||
@@ -216,7 +217,7 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
};
|
||||
|
||||
fetchPoolPrice();
|
||||
}, [api, isApiReady, isOpen, asset0, asset1, asset0Decimals, asset1Decimals]);
|
||||
}, [assetHubApi, isAssetHubReady, isOpen, asset0, asset1, asset0Decimals, asset1Decimals]);
|
||||
|
||||
// Auto-calculate asset1 amount based on asset0 input (only if pool has liquidity)
|
||||
useEffect(() => {
|
||||
@@ -230,7 +231,7 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
}, [amount0, currentPrice, asset1Decimals, isPoolEmpty]);
|
||||
|
||||
const handleAddLiquidity = async () => {
|
||||
if (!api || !selectedAccount || !amount0 || !amount1) return;
|
||||
if (!assetHubApi || !selectedAccount || !amount0 || !amount1) return;
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
@@ -287,9 +288,9 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
|
||||
// If asset0 is HEZ (0), need to wrap it first
|
||||
if (asset0 === 0 || asset0 === ASSET_IDS.WHEZ) {
|
||||
const wrapTx = api.tx.tokenWrapper.wrap(amount0BN.toString());
|
||||
const wrapTx = assetHubApi.tx.tokenWrapper.wrap(amount0BN.toString());
|
||||
|
||||
const addLiquidityTx = api.tx.assetConversion.addLiquidity(
|
||||
const addLiquidityTx = assetHubApi.tx.assetConversion.addLiquidity(
|
||||
asset0,
|
||||
asset1,
|
||||
amount0BN.toString(),
|
||||
@@ -300,10 +301,10 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
);
|
||||
|
||||
// Batch wrap + add liquidity
|
||||
tx = api.tx.utility.batchAll([wrapTx, addLiquidityTx]);
|
||||
tx = assetHubApi.tx.utility.batchAll([wrapTx, addLiquidityTx]);
|
||||
} else {
|
||||
// Direct add liquidity (no wrapping needed)
|
||||
tx = api.tx.assetConversion.addLiquidity(
|
||||
tx = assetHubApi.tx.assetConversion.addLiquidity(
|
||||
asset0,
|
||||
asset1,
|
||||
amount0BN.toString(),
|
||||
@@ -325,7 +326,7 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
|
||||
// Check for errors
|
||||
const hasError = events.some(({ event }) =>
|
||||
api.events.system.ExtrinsicFailed.is(event)
|
||||
assetHubApi.events.system.ExtrinsicFailed.is(event)
|
||||
);
|
||||
|
||||
if (hasError || dispatchError) {
|
||||
@@ -333,7 +334,7 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
|
||||
if (dispatchError) {
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = api.registry.findMetaError(dispatchError.asModule);
|
||||
const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule);
|
||||
const { docs, name, section } = decoded;
|
||||
errorMessage = `${section}.${name}: ${docs.join(' ')}`;
|
||||
if (import.meta.env.DEV) console.error('Dispatch error:', errorMessage);
|
||||
@@ -344,7 +345,7 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
}
|
||||
|
||||
events.forEach(({ event }) => {
|
||||
if (api.events.system.ExtrinsicFailed.is(event)) {
|
||||
if (assetHubApi.events.system.ExtrinsicFailed.is(event)) {
|
||||
if (import.meta.env.DEV) console.error('ExtrinsicFailed event:', event.toHuman());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,24 +8,20 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { usePezkuwi } from '@/contexts/PezkuwiContext';
|
||||
import { ASSET_IDS, getAssetSymbol } from '@pezkuwi/lib/wallet';
|
||||
import { NATIVE_TOKEN_ID } from '@/types/dex';
|
||||
import { AddLiquidityModal } from '@/components/AddLiquidityModal';
|
||||
import { RemoveLiquidityModal } from '@/components/RemoveLiquidityModal';
|
||||
|
||||
// Helper function to convert asset IDs to user-friendly display names
|
||||
// Users should only see HEZ, PEZ, USDT - wrapped tokens are backend details
|
||||
const getDisplayTokenName = (assetId: number): string => {
|
||||
if (assetId === ASSET_IDS.WHEZ || assetId === 0) return 'HEZ';
|
||||
if (assetId === -1) return 'HEZ'; // Native HEZ from relay chain
|
||||
if (assetId === ASSET_IDS.WHEZ || assetId === 2) return 'wHEZ';
|
||||
if (assetId === ASSET_IDS.PEZ || assetId === 1) return 'PEZ';
|
||||
if (assetId === ASSET_IDS.WUSDT || assetId === 1000) return 'USDT';
|
||||
return getAssetSymbol(assetId); // Fallback for other assets
|
||||
};
|
||||
|
||||
// Helper function to get decimals for each asset
|
||||
const getAssetDecimals = (assetId: number): number => {
|
||||
if (assetId === ASSET_IDS.WUSDT) return 6; // wUSDT has 6 decimals
|
||||
return 12; // wHEZ, PEZ have 12 decimals
|
||||
};
|
||||
|
||||
interface PoolData {
|
||||
asset0: number;
|
||||
asset1: number;
|
||||
@@ -43,7 +39,8 @@ interface LPPosition {
|
||||
}
|
||||
|
||||
const PoolDashboard = () => {
|
||||
const { api, isApiReady, selectedAccount } = usePezkuwi();
|
||||
// Use Asset Hub API for DEX operations (assetConversion pallet is on Asset Hub)
|
||||
const { assetHubApi, isAssetHubReady, selectedAccount } = usePezkuwi();
|
||||
|
||||
const [poolData, setPoolData] = useState<PoolData | null>(null);
|
||||
const [lpPosition, setLPPosition] = useState<LPPosition | null>(null);
|
||||
@@ -56,30 +53,38 @@ const PoolDashboard = () => {
|
||||
const [availablePools, setAvailablePools] = useState<Array<[number, number]>>([]);
|
||||
const [selectedPool, setSelectedPool] = useState<string>('0-1'); // Default: wHEZ/PEZ
|
||||
|
||||
// Helper to convert asset ID to XCM Location format (same as CreatePoolModal)
|
||||
const formatAssetId = (id: number) => {
|
||||
if (id === NATIVE_TOKEN_ID) {
|
||||
// Native token from relay chain - XCM location format
|
||||
return { parents: 1, interior: 'Here' };
|
||||
}
|
||||
// Asset on Asset Hub - XCM location format with PalletInstance 50 (assets pallet)
|
||||
return { parents: 0, interior: { X2: [{ PalletInstance: 50 }, { GeneralIndex: id }] } };
|
||||
};
|
||||
|
||||
// Discover available pools
|
||||
useEffect(() => {
|
||||
if (!api || !isApiReady) return;
|
||||
if (!assetHubApi || !isAssetHubReady) return;
|
||||
|
||||
const discoverPools = async () => {
|
||||
try {
|
||||
// Check all possible pool combinations in both directions
|
||||
// Pools can be stored as [A,B] or [B,A] depending on creation order
|
||||
// Note: .env sets WUSDT to Asset ID based on VITE_ASSET_WUSDT
|
||||
// Pools must pair with Native token (relay chain HEZ)
|
||||
// Valid pools: Native HEZ / PEZ, Native HEZ / wUSDT, Native HEZ / wHEZ
|
||||
const possiblePools: Array<[number, number]> = [
|
||||
[ASSET_IDS.WHEZ, ASSET_IDS.PEZ], // wHEZ(0) / PEZ(1) -> Shows as HEZ-PEZ
|
||||
[ASSET_IDS.PEZ, ASSET_IDS.WHEZ], // PEZ(1) / wHEZ(0) -> Shows as HEZ-PEZ (reverse)
|
||||
[ASSET_IDS.WHEZ, ASSET_IDS.WUSDT], // wHEZ(0) / wUSDT -> Shows as HEZ-USDT
|
||||
[ASSET_IDS.WUSDT, ASSET_IDS.WHEZ], // wUSDT / wHEZ(0) -> Shows as HEZ-USDT (reverse)
|
||||
[ASSET_IDS.PEZ, ASSET_IDS.WUSDT], // PEZ(1) / wUSDT -> Shows as PEZ-USDT
|
||||
[ASSET_IDS.WUSDT, ASSET_IDS.PEZ], // wUSDT / PEZ(1) -> Shows as PEZ-USDT (reverse)
|
||||
[NATIVE_TOKEN_ID, ASSET_IDS.PEZ], // Native HEZ / PEZ
|
||||
[NATIVE_TOKEN_ID, ASSET_IDS.WUSDT], // Native HEZ / wUSDT
|
||||
[NATIVE_TOKEN_ID, ASSET_IDS.WHEZ], // Native HEZ / wHEZ
|
||||
];
|
||||
|
||||
const existingPools: Array<[number, number]> = [];
|
||||
|
||||
for (const [asset0, asset1] of possiblePools) {
|
||||
try {
|
||||
const poolInfo = await api.query.assetConversion.pools([asset0, asset1]);
|
||||
if (poolInfo.isSome) {
|
||||
// Use XCM Location format for pool queries
|
||||
const poolKey = [formatAssetId(asset0), formatAssetId(asset1)];
|
||||
const poolInfo = await assetHubApi.query.assetConversion.pools(poolKey);
|
||||
if ((poolInfo as { isSome: boolean }).isSome) {
|
||||
existingPools.push([asset0, asset1]);
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(`✅ Found pool: ${asset0}-${asset1}`);
|
||||
@@ -116,69 +121,42 @@ const PoolDashboard = () => {
|
||||
};
|
||||
|
||||
discoverPools();
|
||||
}, [api, isApiReady, selectedPool]);
|
||||
}, [assetHubApi, isAssetHubReady, selectedPool]);
|
||||
|
||||
|
||||
// Fetch pool data
|
||||
useEffect(() => {
|
||||
if (!api || !isApiReady || !selectedPool) return;
|
||||
if (!assetHubApi || !isAssetHubReady || !selectedPool) return;
|
||||
|
||||
const fetchPoolData = async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// Parse selected pool (e.g., "1-2" -> [1, 2])
|
||||
const [asset1Str, asset2Str] = selectedPool.split('-');
|
||||
const asset1 = parseInt(asset1Str);
|
||||
// Parse selected pool (e.g., "-1-1" -> [-1, 1] for Native HEZ / PEZ)
|
||||
const [asset1Str, asset2Str] = selectedPool.split('-').filter(s => s !== '');
|
||||
const asset1 = selectedPool.startsWith('-') ? -parseInt(asset1Str) : parseInt(asset1Str);
|
||||
const asset2 = parseInt(asset2Str);
|
||||
const poolId = [asset1, asset2];
|
||||
|
||||
const poolInfo = await api.query.assetConversion.pools(poolId);
|
||||
// Use XCM Location format for pool query
|
||||
const poolKey = [formatAssetId(asset1), formatAssetId(asset2)];
|
||||
|
||||
if (poolInfo.isSome) {
|
||||
const lpTokenData = poolInfo.unwrap().toJSON() as Record<string, unknown>;
|
||||
const lpTokenId = lpTokenData.lpToken;
|
||||
const poolInfo = await assetHubApi.query.assetConversion.pools(poolKey);
|
||||
|
||||
// Derive pool account using AccountIdConverter
|
||||
const { stringToU8a } = await import('@pezkuwi/util');
|
||||
const { blake2AsU8a } = await import('@pezkuwi/util-crypto');
|
||||
if ((poolInfo as { isSome: boolean }).isSome) {
|
||||
const lpTokenData = (poolInfo as { unwrap: () => { toJSON: () => Record<string, unknown> } }).unwrap().toJSON();
|
||||
const lpTokenId = lpTokenData.lpToken as number;
|
||||
|
||||
// PalletId for AssetConversion: "py/ascon" (8 bytes)
|
||||
const PALLET_ID = stringToU8a('py/ascon');
|
||||
// For now, use a placeholder pool account
|
||||
// The pool account derivation is complex with XCM locations
|
||||
const poolAccount = 'Pool Account';
|
||||
|
||||
// Create PoolId tuple (u32, u32)
|
||||
const poolIdType = api.createType('(u32, u32)', [asset1, asset2]);
|
||||
|
||||
// Create (PalletId, PoolId) tuple: ([u8; 8], (u32, u32))
|
||||
const palletIdType = api.createType('[u8; 8]', PALLET_ID);
|
||||
const fullTuple = api.createType('([u8; 8], (u32, u32))', [palletIdType, poolIdType]);
|
||||
|
||||
// Hash the SCALE-encoded tuple
|
||||
const accountHash = blake2AsU8a(fullTuple.toU8a(), 256);
|
||||
const poolAccountId = api.createType('AccountId32', accountHash);
|
||||
const poolAccount = poolAccountId.toString();
|
||||
|
||||
// Get reserves
|
||||
const asset0BalanceData = await api.query.assets.account(asset1, poolAccountId);
|
||||
const asset1BalanceData = await api.query.assets.account(asset2, poolAccountId);
|
||||
|
||||
let reserve0 = 0;
|
||||
let reserve1 = 0;
|
||||
|
||||
// Use dynamic decimals for each asset
|
||||
const asset1Decimals = getAssetDecimals(asset1);
|
||||
const asset2Decimals = getAssetDecimals(asset2);
|
||||
|
||||
if (asset0BalanceData.isSome) {
|
||||
const asset0Data = asset0BalanceData.unwrap().toJSON() as Record<string, unknown>;
|
||||
reserve0 = Number(asset0Data.balance) / Math.pow(10, asset1Decimals);
|
||||
}
|
||||
|
||||
if (asset1BalanceData.isSome) {
|
||||
const asset1Data = asset1BalanceData.unwrap().toJSON() as Record<string, unknown>;
|
||||
reserve1 = Number(asset1Data.balance) / Math.pow(10, asset2Decimals);
|
||||
}
|
||||
// Get reserves - for Native token, query system.account on the pool
|
||||
// For assets, query assets.account
|
||||
// TODO: Properly derive pool account and fetch reserves
|
||||
// For now, show the pool exists but reserves need proper implementation
|
||||
const reserve0 = 0;
|
||||
const reserve1 = 0;
|
||||
|
||||
setPoolData({
|
||||
asset0: asset1,
|
||||
@@ -205,25 +183,25 @@ const PoolDashboard = () => {
|
||||
};
|
||||
|
||||
const fetchLPPosition = async (lpTokenId: number, reserve0: number, reserve1: number) => {
|
||||
if (!api || !selectedAccount) return;
|
||||
if (!assetHubApi || !selectedAccount) return;
|
||||
|
||||
try {
|
||||
// Query user's LP token balance
|
||||
const lpBalance = await api.query.poolAssets.account(lpTokenId, selectedAccount.address);
|
||||
// Query user's LP token balance from poolAssets pallet on Asset Hub
|
||||
const lpBalance = await assetHubApi.query.poolAssets.account(lpTokenId, selectedAccount.address);
|
||||
|
||||
if (lpBalance.isSome) {
|
||||
const lpData = lpBalance.unwrap().toJSON() as Record<string, unknown>;
|
||||
if ((lpBalance as { isSome: boolean }).isSome) {
|
||||
const lpData = (lpBalance as { unwrap: () => { toJSON: () => Record<string, unknown> } }).unwrap().toJSON();
|
||||
const userLpBalance = Number(lpData.balance) / 1e12;
|
||||
|
||||
// Query total LP supply
|
||||
const lpAssetData = await api.query.poolAssets.asset(lpTokenId);
|
||||
const lpAssetData = await assetHubApi.query.poolAssets.asset(lpTokenId);
|
||||
|
||||
if (lpAssetData.isSome) {
|
||||
const assetInfo = lpAssetData.unwrap().toJSON() as Record<string, unknown>;
|
||||
if ((lpAssetData as { isSome: boolean }).isSome) {
|
||||
const assetInfo = (lpAssetData as { unwrap: () => { toJSON: () => Record<string, unknown> } }).unwrap().toJSON();
|
||||
const totalSupply = Number(assetInfo.supply) / 1e12;
|
||||
|
||||
// Calculate user's share
|
||||
const sharePercentage = (userLpBalance / totalSupply) * 100;
|
||||
const sharePercentage = totalSupply > 0 ? (userLpBalance / totalSupply) * 100 : 0;
|
||||
|
||||
// Calculate user's actual token amounts
|
||||
const asset0Amount = (sharePercentage / 100) * reserve0;
|
||||
@@ -248,7 +226,7 @@ const PoolDashboard = () => {
|
||||
const interval = setInterval(fetchPoolData, 30000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [api, isApiReady, selectedAccount, selectedPool]);
|
||||
}, [assetHubApi, isAssetHubReady, selectedAccount, selectedPool]);
|
||||
|
||||
// Calculate metrics
|
||||
const constantProduct = poolData ? poolData.reserve0 * poolData.reserve1 : 0;
|
||||
|
||||
@@ -42,7 +42,8 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
asset0,
|
||||
asset1,
|
||||
}) => {
|
||||
const { api, selectedAccount } = usePezkuwi();
|
||||
// Use Asset Hub API for DEX operations (assetConversion pallet is on Asset Hub)
|
||||
const { assetHubApi, selectedAccount } = usePezkuwi();
|
||||
const { refreshBalances } = useWallet();
|
||||
|
||||
const [percentage, setPercentage] = useState(100);
|
||||
@@ -55,7 +56,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
|
||||
// Fetch minimum balances for both assets
|
||||
useEffect(() => {
|
||||
if (!api || !isOpen) return;
|
||||
if (!assetHubApi || !isOpen) return;
|
||||
|
||||
const fetchMinBalances = async () => {
|
||||
try {
|
||||
@@ -67,7 +68,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
|
||||
if (asset0 === ASSET_IDS.WHEZ || asset0 === 0) {
|
||||
// wHEZ is an asset in the assets pallet
|
||||
const assetDetails0 = await api.query.assets.asset(ASSET_IDS.WHEZ);
|
||||
const assetDetails0 = await assetHubApi.query.assets.asset(ASSET_IDS.WHEZ);
|
||||
if (assetDetails0.isSome) {
|
||||
const details0 = assetDetails0.unwrap().toJSON() as Record<string, unknown>;
|
||||
const min0 = Number(details0.minBalance) / Math.pow(10, getAssetDecimals(asset0));
|
||||
@@ -76,7 +77,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
}
|
||||
} else {
|
||||
// Other assets (PEZ, wUSDT, etc.)
|
||||
const assetDetails0 = await api.query.assets.asset(asset0);
|
||||
const assetDetails0 = await assetHubApi.query.assets.asset(asset0);
|
||||
if (assetDetails0.isSome) {
|
||||
const details0 = assetDetails0.unwrap().toJSON() as Record<string, unknown>;
|
||||
const min0 = Number(details0.minBalance) / Math.pow(10, getAssetDecimals(asset0));
|
||||
@@ -87,7 +88,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
|
||||
if (asset1 === ASSET_IDS.WHEZ || asset1 === 0) {
|
||||
// wHEZ is an asset in the assets pallet
|
||||
const assetDetails1 = await api.query.assets.asset(ASSET_IDS.WHEZ);
|
||||
const assetDetails1 = await assetHubApi.query.assets.asset(ASSET_IDS.WHEZ);
|
||||
if (assetDetails1.isSome) {
|
||||
const details1 = assetDetails1.unwrap().toJSON() as Record<string, unknown>;
|
||||
const min1 = Number(details1.minBalance) / Math.pow(10, getAssetDecimals(asset1));
|
||||
@@ -96,7 +97,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
}
|
||||
} else {
|
||||
// Other assets (PEZ, wUSDT, etc.)
|
||||
const assetDetails1 = await api.query.assets.asset(asset1);
|
||||
const assetDetails1 = await assetHubApi.query.assets.asset(asset1);
|
||||
if (assetDetails1.isSome) {
|
||||
const details1 = assetDetails1.unwrap().toJSON() as Record<string, unknown>;
|
||||
const min1 = Number(details1.minBalance) / Math.pow(10, getAssetDecimals(asset1));
|
||||
@@ -110,7 +111,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
};
|
||||
|
||||
fetchMinBalances();
|
||||
}, [api, isOpen, asset0, asset1]);
|
||||
}, [assetHubApi, isOpen, asset0, asset1]);
|
||||
|
||||
// Calculate maximum removable percentage based on minBalance requirements
|
||||
useEffect(() => {
|
||||
@@ -132,7 +133,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
}, [minBalance0, minBalance1, lpPosition.asset0Amount, lpPosition.asset1Amount]);
|
||||
|
||||
const handleRemoveLiquidity = async () => {
|
||||
if (!api || !selectedAccount) return;
|
||||
if (!assetHubApi || !selectedAccount) return;
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
@@ -157,7 +158,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
const minAsset1BN = (expectedAsset1BN * BigInt(95)) / BigInt(100);
|
||||
|
||||
// Remove liquidity transaction
|
||||
const removeLiquidityTx = api.tx.assetConversion.removeLiquidity(
|
||||
const removeLiquidityTx = assetHubApi.tx.assetConversion.removeLiquidity(
|
||||
asset0,
|
||||
asset1,
|
||||
lpToRemoveBN.toString(),
|
||||
@@ -173,9 +174,9 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
if (hasWHEZ) {
|
||||
// Unwrap wHEZ back to HEZ
|
||||
const whezAmount = asset0 === ASSET_IDS.WHEZ ? minAsset0BN : minAsset1BN;
|
||||
const unwrapTx = api.tx.tokenWrapper.unwrap(whezAmount.toString());
|
||||
const unwrapTx = assetHubApi.tx.tokenWrapper.unwrap(whezAmount.toString());
|
||||
// Batch transactions: removeLiquidity + unwrap
|
||||
tx = api.tx.utility.batchAll([removeLiquidityTx, unwrapTx]);
|
||||
tx = assetHubApi.tx.utility.batchAll([removeLiquidityTx, unwrapTx]);
|
||||
} else {
|
||||
// No unwrap needed for pools without wHEZ
|
||||
tx = removeLiquidityTx;
|
||||
@@ -192,7 +193,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
|
||||
// Check for errors
|
||||
const hasError = events.some(({ event }) =>
|
||||
api.events.system.ExtrinsicFailed.is(event)
|
||||
assetHubApi.events.system.ExtrinsicFailed.is(event)
|
||||
);
|
||||
|
||||
if (hasError) {
|
||||
|
||||
@@ -22,7 +22,8 @@ const AVAILABLE_TOKENS = [
|
||||
] as const;
|
||||
|
||||
const TokenSwap = () => {
|
||||
const { api, isApiReady, selectedAccount } = usePezkuwi();
|
||||
// Use Asset Hub API for DEX operations (assetConversion pallet is on Asset Hub)
|
||||
const { assetHubApi, isAssetHubReady, selectedAccount } = usePezkuwi();
|
||||
const { balances, refreshBalances } = useWallet();
|
||||
const { toast } = useToast();
|
||||
|
||||
@@ -149,9 +150,9 @@ const TokenSwap = () => {
|
||||
|
||||
// Check if AssetConversion pallet is available
|
||||
useEffect(() => {
|
||||
if (import.meta.env.DEV) console.log('🔍 Checking DEX availability...', { api: !!api, isApiReady });
|
||||
if (api && isApiReady) {
|
||||
const hasAssetConversion = api.tx.assetConversion !== undefined;
|
||||
if (import.meta.env.DEV) console.log('🔍 Checking DEX availability...', { assetHubApi: !!assetHubApi, isAssetHubReady });
|
||||
if (api && isAssetHubReady) {
|
||||
const hasAssetConversion = assetHubApi.tx.assetConversion !== undefined;
|
||||
if (import.meta.env.DEV) console.log('🔍 AssetConversion pallet check:', hasAssetConversion);
|
||||
setIsDexAvailable(hasAssetConversion);
|
||||
|
||||
@@ -161,16 +162,16 @@ const TokenSwap = () => {
|
||||
if (import.meta.env.DEV) console.log('✅ AssetConversion pallet is available!');
|
||||
}
|
||||
}
|
||||
}, [api, isApiReady]);
|
||||
}, [assetHubApi, isAssetHubReady]);
|
||||
|
||||
// Fetch exchange rate from AssetConversion pool
|
||||
// Always use wHEZ/PEZ pool (the only valid pool)
|
||||
useEffect(() => {
|
||||
const fetchExchangeRate = async () => {
|
||||
if (import.meta.env.DEV) console.log('🔍 fetchExchangeRate check:', { api: !!api, isApiReady, isDexAvailable, fromToken, toToken });
|
||||
if (import.meta.env.DEV) console.log('🔍 fetchExchangeRate check:', { assetHubApi: !!assetHubApi, isAssetHubReady, isDexAvailable, fromToken, toToken });
|
||||
|
||||
if (!api || !isApiReady || !isDexAvailable) {
|
||||
if (import.meta.env.DEV) console.log('⚠️ Skipping fetchExchangeRate:', { api: !!api, isApiReady, isDexAvailable });
|
||||
if (!assetHubApi || !isAssetHubReady || !isDexAvailable) {
|
||||
if (import.meta.env.DEV) console.log('⚠️ Skipping fetchExchangeRate:', { assetHubApi: !!assetHubApi, isAssetHubReady, isDexAvailable });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -207,7 +208,7 @@ const TokenSwap = () => {
|
||||
if (import.meta.env.DEV) console.log('🔍 Pool query with:', poolAssets);
|
||||
|
||||
// Query pool from AssetConversion pallet
|
||||
const poolInfo = await api.query.assetConversion.pools(poolAssets);
|
||||
const poolInfo = await assetHubApi.query.assetConversion.pools(poolAssets);
|
||||
if (import.meta.env.DEV) console.log('🔍 Pool query result:', poolInfo.toHuman());
|
||||
|
||||
if (import.meta.env.DEV) console.log('🔍 Pool isEmpty?', poolInfo.isEmpty, 'exists?', !poolInfo.isEmpty);
|
||||
@@ -228,12 +229,12 @@ const TokenSwap = () => {
|
||||
const PALLET_ID = stringToU8a('py/ascon');
|
||||
|
||||
// Create PoolId tuple (u32, u32)
|
||||
const poolId = api.createType('(u32, u32)', [asset1, asset2]);
|
||||
const poolId = assetHubApi.createType('(u32, u32)', [asset1, asset2]);
|
||||
if (import.meta.env.DEV) console.log('🔍 Pool ID:', poolId.toHuman());
|
||||
|
||||
// Create (PalletId, PoolId) tuple: ([u8; 8], (u32, u32))
|
||||
const palletIdType = api.createType('[u8; 8]', PALLET_ID);
|
||||
const fullTuple = api.createType('([u8; 8], (u32, u32))', [palletIdType, poolId]);
|
||||
const palletIdType = assetHubApi.createType('[u8; 8]', PALLET_ID);
|
||||
const fullTuple = assetHubApi.createType('([u8; 8], (u32, u32))', [palletIdType, poolId]);
|
||||
|
||||
if (import.meta.env.DEV) console.log('🔍 Full tuple encoded length:', fullTuple.toU8a().length);
|
||||
if (import.meta.env.DEV) console.log('🔍 Full tuple bytes:', Array.from(fullTuple.toU8a()));
|
||||
@@ -242,13 +243,13 @@ const TokenSwap = () => {
|
||||
const accountHash = blake2AsU8a(fullTuple.toU8a(), 256);
|
||||
if (import.meta.env.DEV) console.log('🔍 Account hash:', Array.from(accountHash).slice(0, 8));
|
||||
|
||||
const poolAccountId = api.createType('AccountId32', accountHash);
|
||||
const poolAccountId = assetHubApi.createType('AccountId32', accountHash);
|
||||
if (import.meta.env.DEV) console.log('🔍 Pool AccountId (NEW METHOD):', poolAccountId.toString());
|
||||
|
||||
// Query pool account's asset balances
|
||||
if (import.meta.env.DEV) console.log('🔍 Querying reserves for asset', asset1, 'and', asset2);
|
||||
const reserve0Query = await api.query.assets.account(asset1, poolAccountId);
|
||||
const reserve1Query = await api.query.assets.account(asset2, poolAccountId);
|
||||
const reserve0Query = await assetHubApi.query.assets.account(asset1, poolAccountId);
|
||||
const reserve1Query = await assetHubApi.query.assets.account(asset2, poolAccountId);
|
||||
|
||||
if (import.meta.env.DEV) console.log('🔍 Reserve0 query result:', reserve0Query.toHuman());
|
||||
if (import.meta.env.DEV) console.log('🔍 Reserve1 query result:', reserve1Query.toHuman());
|
||||
@@ -316,19 +317,19 @@ const TokenSwap = () => {
|
||||
};
|
||||
|
||||
fetchExchangeRate();
|
||||
}, [api, isApiReady, isDexAvailable, fromToken, toToken]);
|
||||
}, [assetHubApi, isAssetHubReady, isDexAvailable, fromToken, toToken]);
|
||||
|
||||
// Fetch liquidity pools
|
||||
useEffect(() => {
|
||||
const fetchLiquidityPools = async () => {
|
||||
if (!api || !isApiReady || !isDexAvailable) {
|
||||
if (!assetHubApi || !isAssetHubReady || !isDexAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoadingPools(true);
|
||||
try {
|
||||
// Query all pools from AssetConversion pallet
|
||||
const poolsEntries = await api.query.assetConversion.pools.entries();
|
||||
const poolsEntries = await assetHubApi.query.assetConversion.pools.entries();
|
||||
|
||||
if (poolsEntries && poolsEntries.length > 0) {
|
||||
const pools = poolsEntries.map(([key, value]: [unknown, unknown]) => {
|
||||
@@ -366,20 +367,20 @@ const TokenSwap = () => {
|
||||
};
|
||||
|
||||
fetchLiquidityPools();
|
||||
}, [api, isApiReady, isDexAvailable]);
|
||||
}, [assetHubApi, isAssetHubReady, isDexAvailable]);
|
||||
|
||||
// Fetch swap transaction history
|
||||
useEffect(() => {
|
||||
const fetchSwapHistory = async () => {
|
||||
if (!api || !isApiReady || !isDexAvailable || !selectedAccount) {
|
||||
if (!assetHubApi || !isAssetHubReady || !isDexAvailable || !selectedAccount) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoadingHistory(true);
|
||||
try {
|
||||
// Get recent finalized blocks (last 100 blocks)
|
||||
const finalizedHead = await api.rpc.chain.getFinalizedHead();
|
||||
const finalizedBlock = await api.rpc.chain.getBlock(finalizedHead);
|
||||
const finalizedHead = await assetHubApi.rpc.chain.getFinalizedHead();
|
||||
const finalizedBlock = await assetHubApi.rpc.chain.getBlock(finalizedHead);
|
||||
const currentBlockNumber = finalizedBlock.block.header.number.toNumber();
|
||||
|
||||
const startBlock = Math.max(0, currentBlockNumber - 100);
|
||||
@@ -391,17 +392,17 @@ const TokenSwap = () => {
|
||||
// Query block by block for SwapExecuted events
|
||||
for (let blockNum = currentBlockNumber; blockNum >= startBlock && transactions.length < 10; blockNum--) {
|
||||
try {
|
||||
const blockHash = await api.rpc.chain.getBlockHash(blockNum);
|
||||
const apiAt = await api.at(blockHash);
|
||||
const blockHash = await assetHubApi.rpc.chain.getBlockHash(blockNum);
|
||||
const apiAt = await assetHubApi.at(blockHash);
|
||||
const events = await apiAt.query.system.events();
|
||||
//const block = await api.rpc.chain.getBlock(blockHash);
|
||||
//const block = await assetHubApi.rpc.chain.getBlock(blockHash);
|
||||
const timestamp = Date.now() - ((currentBlockNumber - blockNum) * 6000); // Estimate 6s per block
|
||||
|
||||
events.forEach((record: { event: { data: unknown[] } }) => {
|
||||
const { event } = record;
|
||||
|
||||
// Check for AssetConversion::SwapExecuted event
|
||||
if (api.events.assetConversion?.SwapExecuted?.is(event)) {
|
||||
if (assetHubApi.events.assetConversion?.SwapExecuted?.is(event)) {
|
||||
// SwapExecuted has 5 fields: (who, send_to, amountIn, amountOut, path)
|
||||
const [who, , amountIn, amountOut, path] = event.data;
|
||||
|
||||
@@ -464,7 +465,7 @@ const TokenSwap = () => {
|
||||
};
|
||||
|
||||
fetchSwapHistory();
|
||||
}, [api, isApiReady, isDexAvailable, selectedAccount]);
|
||||
}, [assetHubApi, isAssetHubReady, isDexAvailable, selectedAccount]);
|
||||
|
||||
const handleSwap = () => {
|
||||
setFromToken(toToken);
|
||||
@@ -473,7 +474,7 @@ const TokenSwap = () => {
|
||||
};
|
||||
|
||||
const handleConfirmSwap = async () => {
|
||||
if (!api || !selectedAccount) {
|
||||
if (!assetHubApi || !selectedAccount) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Please connect your wallet',
|
||||
@@ -551,61 +552,61 @@ const TokenSwap = () => {
|
||||
|
||||
if (fromToken === 'HEZ' && toToken === 'PEZ') {
|
||||
// HEZ → PEZ: wrap(HEZ→wHEZ) then swap(wHEZ→PEZ)
|
||||
const wrapTx = api.tx.tokenWrapper.wrap(amountIn.toString());
|
||||
const wrapTx = assetHubApi.tx.tokenWrapper.wrap(amountIn.toString());
|
||||
// AssetKind = u32, so swap path is just [0, 1]
|
||||
const swapPath = [0, 1]; // wHEZ → PEZ
|
||||
const swapTx = api.tx.assetConversion.swapExactTokensForTokens(
|
||||
const swapTx = assetHubApi.tx.assetConversion.swapExactTokensForTokens(
|
||||
swapPath,
|
||||
amountIn.toString(),
|
||||
minAmountOut.toString(),
|
||||
selectedAccount.address,
|
||||
true
|
||||
);
|
||||
tx = api.tx.utility.batchAll([wrapTx, swapTx]);
|
||||
tx = assetHubApi.tx.utility.batchAll([wrapTx, swapTx]);
|
||||
|
||||
} else if (fromToken === 'PEZ' && toToken === 'HEZ') {
|
||||
// PEZ → HEZ: swap(PEZ→wHEZ) then unwrap(wHEZ→HEZ)
|
||||
// AssetKind = u32, so swap path is just [1, 0]
|
||||
const swapPath = [1, 0]; // PEZ → wHEZ
|
||||
const swapTx = api.tx.assetConversion.swapExactTokensForTokens(
|
||||
const swapTx = assetHubApi.tx.assetConversion.swapExactTokensForTokens(
|
||||
swapPath,
|
||||
amountIn.toString(),
|
||||
minAmountOut.toString(),
|
||||
selectedAccount.address,
|
||||
true
|
||||
);
|
||||
const unwrapTx = api.tx.tokenWrapper.unwrap(minAmountOut.toString());
|
||||
tx = api.tx.utility.batchAll([swapTx, unwrapTx]);
|
||||
const unwrapTx = assetHubApi.tx.tokenWrapper.unwrap(minAmountOut.toString());
|
||||
tx = assetHubApi.tx.utility.batchAll([swapTx, unwrapTx]);
|
||||
|
||||
} else if (fromToken === 'HEZ') {
|
||||
// HEZ → Any Asset: wrap(HEZ→wHEZ) then swap(wHEZ→Asset)
|
||||
const wrapTx = api.tx.tokenWrapper.wrap(amountIn.toString());
|
||||
const wrapTx = assetHubApi.tx.tokenWrapper.wrap(amountIn.toString());
|
||||
// Map token symbol to asset ID
|
||||
const toAssetId = toToken === 'PEZ' ? 1 : toToken === 'USDT' ? 1000 : ASSET_IDS[toToken as keyof typeof ASSET_IDS];
|
||||
const swapPath = [0, toAssetId]; // wHEZ → target asset
|
||||
const swapTx = api.tx.assetConversion.swapExactTokensForTokens(
|
||||
const swapTx = assetHubApi.tx.assetConversion.swapExactTokensForTokens(
|
||||
swapPath,
|
||||
amountIn.toString(),
|
||||
minAmountOut.toString(),
|
||||
selectedAccount.address,
|
||||
true
|
||||
);
|
||||
tx = api.tx.utility.batchAll([wrapTx, swapTx]);
|
||||
tx = assetHubApi.tx.utility.batchAll([wrapTx, swapTx]);
|
||||
|
||||
} else if (toToken === 'HEZ') {
|
||||
// Any Asset → HEZ: swap(Asset→wHEZ) then unwrap(wHEZ→HEZ)
|
||||
// Map token symbol to asset ID
|
||||
const fromAssetId = fromToken === 'PEZ' ? 1 : fromToken === 'USDT' ? 1000 : ASSET_IDS[fromToken as keyof typeof ASSET_IDS];
|
||||
const swapPath = [fromAssetId, 0]; // source asset → wHEZ
|
||||
const swapTx = api.tx.assetConversion.swapExactTokensForTokens(
|
||||
const swapTx = assetHubApi.tx.assetConversion.swapExactTokensForTokens(
|
||||
swapPath,
|
||||
amountIn.toString(),
|
||||
minAmountOut.toString(),
|
||||
selectedAccount.address,
|
||||
true
|
||||
);
|
||||
const unwrapTx = api.tx.tokenWrapper.unwrap(minAmountOut.toString());
|
||||
tx = api.tx.utility.batchAll([swapTx, unwrapTx]);
|
||||
const unwrapTx = assetHubApi.tx.tokenWrapper.unwrap(minAmountOut.toString());
|
||||
tx = assetHubApi.tx.utility.batchAll([swapTx, unwrapTx]);
|
||||
|
||||
} else {
|
||||
// Direct swap between assets (PEZ ↔ USDT, etc.)
|
||||
@@ -614,7 +615,7 @@ const TokenSwap = () => {
|
||||
const toAssetId = toToken === 'PEZ' ? 1 : toToken === 'USDT' ? 1000 : ASSET_IDS[toToken as keyof typeof ASSET_IDS];
|
||||
const swapPath = [fromAssetId, toAssetId];
|
||||
|
||||
tx = api.tx.assetConversion.swapExactTokensForTokens(
|
||||
tx = assetHubApi.tx.assetConversion.swapExactTokensForTokens(
|
||||
swapPath,
|
||||
amountIn.toString(),
|
||||
minAmountOut.toString(),
|
||||
@@ -649,7 +650,7 @@ const TokenSwap = () => {
|
||||
let errorMessage = 'Transaction failed';
|
||||
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = api.registry.findMetaError(dispatchError.asModule);
|
||||
const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule);
|
||||
errorMessage = `${decoded.section}.${decoded.name}: ${decoded.docs}`;
|
||||
}
|
||||
|
||||
@@ -664,7 +665,7 @@ const TokenSwap = () => {
|
||||
|
||||
// Success - check for swap event
|
||||
const hasSwapEvent = events.some(({ event }) =>
|
||||
api.events.assetConversion?.SwapExecuted?.is(event)
|
||||
assetHubApi.events.assetConversion?.SwapExecuted?.is(event)
|
||||
);
|
||||
|
||||
if (hasSwapEvent || fromToken === 'HEZ' || toToken === 'HEZ') {
|
||||
@@ -683,23 +684,23 @@ const TokenSwap = () => {
|
||||
setTimeout(async () => {
|
||||
if (import.meta.env.DEV) console.log('🔄 Refreshing swap history...');
|
||||
const fetchSwapHistory = async () => {
|
||||
if (!api || !isApiReady || !isDexAvailable || !selectedAccount) return;
|
||||
if (!assetHubApi || !isAssetHubReady || !isDexAvailable || !selectedAccount) return;
|
||||
setIsLoadingHistory(true);
|
||||
try {
|
||||
const finalizedHead = await api.rpc.chain.getFinalizedHead();
|
||||
const finalizedBlock = await api.rpc.chain.getBlock(finalizedHead);
|
||||
const finalizedHead = await assetHubApi.rpc.chain.getFinalizedHead();
|
||||
const finalizedBlock = await assetHubApi.rpc.chain.getBlock(finalizedHead);
|
||||
const currentBlockNumber = finalizedBlock.block.header.number.toNumber();
|
||||
const startBlock = Math.max(0, currentBlockNumber - 100);
|
||||
const transactions: SwapTransaction[] = [];
|
||||
for (let blockNum = currentBlockNumber; blockNum >= startBlock && transactions.length < 10; blockNum--) {
|
||||
try {
|
||||
const blockHash = await api.rpc.chain.getBlockHash(blockNum);
|
||||
const apiAt = await api.at(blockHash);
|
||||
const blockHash = await assetHubApi.rpc.chain.getBlockHash(blockNum);
|
||||
const apiAt = await assetHubApi.at(blockHash);
|
||||
const events = await apiAt.query.system.events();
|
||||
const timestamp = Date.now() - ((currentBlockNumber - blockNum) * 6000);
|
||||
events.forEach((record: { event: { data: unknown[] } }) => {
|
||||
const { event } = record;
|
||||
if (api.events.assetConversion?.SwapExecuted?.is(event)) {
|
||||
if (assetHubApi.events.assetConversion?.SwapExecuted?.is(event)) {
|
||||
// SwapExecuted has 5 fields: (who, send_to, amountIn, amountOut, path)
|
||||
const [who, , amountIn, amountOut, path] = event.data;
|
||||
|
||||
@@ -780,7 +781,7 @@ const TokenSwap = () => {
|
||||
};
|
||||
|
||||
// Show DEX unavailable message
|
||||
if (!isDexAvailable && isApiReady) {
|
||||
if (!isDexAvailable && isAssetHubReady) {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<Card className="p-8">
|
||||
|
||||
@@ -21,7 +21,8 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
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 [lpTokenBalance, setLpTokenBalance] = useState<string>('0');
|
||||
@@ -43,11 +44,11 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
// Fetch LP token balance
|
||||
useEffect(() => {
|
||||
const fetchLPBalance = async () => {
|
||||
if (!api || !isApiReady || !account || !pool) return;
|
||||
if (!assetHubApi || !isAssetHubReady || !account || !pool) return;
|
||||
|
||||
try {
|
||||
// Get pool account
|
||||
const poolAccount = await api.query.assetConversion.pools([
|
||||
const poolAccount = await assetHubApi.query.assetConversion.pools([
|
||||
pool.asset1,
|
||||
pool.asset2,
|
||||
]);
|
||||
@@ -60,8 +61,8 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
// LP token ID is derived from pool ID
|
||||
// For now, we'll query the pool's LP token supply
|
||||
// In a real implementation, you'd need to query the specific LP token for the user
|
||||
if (api.query.assetConversion.nextPoolAssetId) {
|
||||
await api.query.assetConversion.nextPoolAssetId();
|
||||
if (assetHubApi.query.assetConversion.nextPoolAssetId) {
|
||||
await assetHubApi.query.assetConversion.nextPoolAssetId();
|
||||
}
|
||||
|
||||
// This is a simplified version - you'd need to track LP tokens properly
|
||||
@@ -73,7 +74,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
};
|
||||
|
||||
fetchLPBalance();
|
||||
}, [api, isApiReady, account, pool]);
|
||||
}, [assetHubApi, isAssetHubReady, account, pool]);
|
||||
|
||||
const calculateOutputAmounts = () => {
|
||||
if (!pool || BigInt(lpTokenBalance) === BigInt(0)) {
|
||||
@@ -98,7 +99,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
};
|
||||
|
||||
const handleRemoveLiquidity = async () => {
|
||||
if (!api || !isApiReady || !signer || !account || !pool) {
|
||||
if (!assetHubApi || !isAssetHubReady || !signer || !account || !pool) {
|
||||
setErrorMessage('Wallet not connected');
|
||||
return;
|
||||
}
|
||||
@@ -119,7 +120,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
setTxStatus('signing');
|
||||
setErrorMessage('');
|
||||
|
||||
const tx = api.tx.assetConversion.removeLiquidity(
|
||||
const tx = assetHubApi.tx.assetConversion.removeLiquidity(
|
||||
pool.asset1,
|
||||
pool.asset2,
|
||||
lpAmount.toString(),
|
||||
@@ -137,7 +138,7 @@ export const RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
|
||||
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());
|
||||
|
||||
Reference in New Issue
Block a user