mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-22 21:47:56 +00:00
fix: use XCM Location format for native token in liquidity operations
This commit is contained in:
@@ -92,42 +92,39 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
|
||||
const fetchMinimumBalances = async () => {
|
||||
try {
|
||||
// Query asset details which contains minBalance
|
||||
const assetDetails0 = await assetHubApi.query.assets.asset(asset0);
|
||||
const assetDetails1 = await assetHubApi.query.assets.asset(asset1);
|
||||
// For native token (-1), use default minimum - can't query assets pallet
|
||||
let minBalance0 = 0.01;
|
||||
let minBalance1 = 0.01;
|
||||
|
||||
if (import.meta.env.DEV) console.log('🔍 Querying minimum balances for assets:', { asset0, asset1 });
|
||||
|
||||
if (assetDetails0.isSome && assetDetails1.isSome) {
|
||||
const details0 = assetDetails0.unwrap().toJSON() as AssetDetails;
|
||||
const details1 = assetDetails1.unwrap().toJSON() as AssetDetails;
|
||||
|
||||
if (import.meta.env.DEV) console.log('📦 Asset details:', {
|
||||
asset0: details0,
|
||||
asset1: details1
|
||||
});
|
||||
|
||||
const minBalance0Raw = details0.minBalance || '0';
|
||||
const minBalance1Raw = details1.minBalance || '0';
|
||||
|
||||
const minBalance0 = Number(minBalance0Raw) / Math.pow(10, asset0Decimals);
|
||||
const minBalance1 = Number(minBalance1Raw) / Math.pow(10, asset1Decimals);
|
||||
|
||||
if (import.meta.env.DEV) console.log('📊 Minimum deposit requirements from assets:', {
|
||||
asset0: asset0Name,
|
||||
minBalance0Raw,
|
||||
minBalance0,
|
||||
asset1: asset1Name,
|
||||
minBalance1Raw,
|
||||
minBalance1
|
||||
});
|
||||
|
||||
setMinDeposit0(minBalance0);
|
||||
setMinDeposit1(minBalance1);
|
||||
} else {
|
||||
if (import.meta.env.DEV) console.warn('⚠️ Asset details not found, using defaults');
|
||||
// Only query assets pallet for non-native tokens (positive IDs)
|
||||
if (asset0 >= 0) {
|
||||
const assetDetails0 = await assetHubApi.query.assets.asset(asset0);
|
||||
if (assetDetails0.isSome) {
|
||||
const details0 = assetDetails0.unwrap().toJSON() as AssetDetails;
|
||||
const minBalance0Raw = details0.minBalance || '0';
|
||||
minBalance0 = Number(minBalance0Raw) / Math.pow(10, asset0Decimals);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset1 >= 0) {
|
||||
const assetDetails1 = await assetHubApi.query.assets.asset(asset1);
|
||||
if (assetDetails1.isSome) {
|
||||
const details1 = assetDetails1.unwrap().toJSON() as AssetDetails;
|
||||
const minBalance1Raw = details1.minBalance || '0';
|
||||
minBalance1 = Number(minBalance1Raw) / Math.pow(10, asset1Decimals);
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.env.DEV) console.log('📊 Minimum deposit requirements:', {
|
||||
asset0: asset0Name,
|
||||
minBalance0,
|
||||
asset1: asset1Name,
|
||||
minBalance1
|
||||
});
|
||||
|
||||
setMinDeposit0(minBalance0);
|
||||
setMinDeposit1(minBalance1);
|
||||
|
||||
// Also check if there's a MintMinLiquidity constant in assetConversion pallet
|
||||
if (assetHubApi.consts.assetConversion) {
|
||||
const mintMinLiq = assetHubApi.consts.assetConversion.mintMinLiquidity;
|
||||
@@ -152,58 +149,34 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
fetchMinimumBalances();
|
||||
}, [assetHubApi, isAssetHubReady, isOpen, asset0, asset1, asset0Decimals, asset1Decimals, asset0Name, asset1Name]);
|
||||
|
||||
// Helper to convert asset ID to XCM Location format
|
||||
const formatAssetLocation = (id: number) => {
|
||||
if (id === -1) {
|
||||
// Native token from relay chain
|
||||
return { parents: 1, interior: 'Here' };
|
||||
}
|
||||
// Asset on Asset Hub
|
||||
return { parents: 0, interior: { X2: [{ PalletInstance: 50 }, { GeneralIndex: id }] } };
|
||||
};
|
||||
|
||||
// Fetch current pool price
|
||||
useEffect(() => {
|
||||
if (!assetHubApi || !isAssetHubReady || !isOpen) return;
|
||||
|
||||
const fetchPoolPrice = async () => {
|
||||
try {
|
||||
const poolId = [asset0, asset1];
|
||||
const poolInfo = await assetHubApi.query.assetConversion.pools(poolId);
|
||||
// Use XCM Location format for pool queries (required for native token)
|
||||
const poolKey = [formatAssetLocation(asset0), formatAssetLocation(asset1)];
|
||||
const poolInfo = await assetHubApi.query.assetConversion.pools(poolKey);
|
||||
|
||||
if (poolInfo.isSome) {
|
||||
// Derive pool account using AccountIdConverter
|
||||
const { stringToU8a } = await import('@pezkuwi/util');
|
||||
const { blake2AsU8a } = await import('@pezkuwi/util-crypto');
|
||||
|
||||
const PALLET_ID = stringToU8a('py/ascon');
|
||||
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 = assetHubApi.createType('AccountId32', accountHash);
|
||||
|
||||
// Get reserves
|
||||
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;
|
||||
const data1 = balance1Data.unwrap().toJSON() as AssetAccountData;
|
||||
|
||||
const reserve0 = Number(data0.balance) / Math.pow(10, asset0Decimals);
|
||||
const reserve1 = Number(data1.balance) / Math.pow(10, asset1Decimals);
|
||||
|
||||
// Consider pool empty if reserves are less than 1 token (dust amounts)
|
||||
const MINIMUM_LIQUIDITY = 1;
|
||||
if (reserve0 >= MINIMUM_LIQUIDITY && reserve1 >= MINIMUM_LIQUIDITY) {
|
||||
setCurrentPrice(reserve1 / reserve0);
|
||||
setIsPoolEmpty(false);
|
||||
if (import.meta.env.DEV) console.log('Pool has liquidity - auto-calculating ratio:', reserve1 / reserve0);
|
||||
} else {
|
||||
setCurrentPrice(null);
|
||||
setIsPoolEmpty(true);
|
||||
if (import.meta.env.DEV) console.log('Pool is empty or has dust only - manual input allowed');
|
||||
}
|
||||
} else {
|
||||
// No reserves found - pool is empty
|
||||
setCurrentPrice(null);
|
||||
setIsPoolEmpty(true);
|
||||
if (import.meta.env.DEV) console.log('Pool is empty - manual input allowed');
|
||||
}
|
||||
// For simplicity, mark pool as having liquidity if it exists
|
||||
// Real reserve fetching requires complex pool account derivation with XCM locations
|
||||
setIsPoolEmpty(false);
|
||||
setCurrentPrice(1); // Default 1:1 ratio, user can adjust
|
||||
if (import.meta.env.DEV) console.log('Pool exists - using default ratio');
|
||||
} else {
|
||||
// Pool doesn't exist yet - completely empty
|
||||
// Pool doesn't exist yet
|
||||
setCurrentPrice(null);
|
||||
setIsPoolEmpty(true);
|
||||
if (import.meta.env.DEV) console.log('Pool does not exist yet - manual input allowed');
|
||||
@@ -217,7 +190,7 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
};
|
||||
|
||||
fetchPoolPrice();
|
||||
}, [assetHubApi, isAssetHubReady, isOpen, asset0, asset1, asset0Decimals, asset1Decimals]);
|
||||
}, [assetHubApi, isAssetHubReady, isOpen, asset0, asset1]);
|
||||
|
||||
// Auto-calculate asset1 amount based on asset0 input (only if pool has liquidity)
|
||||
useEffect(() => {
|
||||
@@ -283,37 +256,19 @@ export const AddLiquidityModal: React.FC<AddLiquidityModalProps> = ({
|
||||
const minAmount0BN = (amount0BN * BigInt(90)) / BigInt(100);
|
||||
const minAmount1BN = (amount1BN * BigInt(90)) / BigInt(100);
|
||||
|
||||
// Build transaction(s)
|
||||
let tx;
|
||||
// Build transaction using XCM Location format for assets
|
||||
const asset0Location = formatAssetLocation(asset0);
|
||||
const asset1Location = formatAssetLocation(asset1);
|
||||
|
||||
// If asset0 is HEZ (0), need to wrap it first
|
||||
if (asset0 === 0 || asset0 === ASSET_IDS.WHEZ) {
|
||||
const wrapTx = assetHubApi.tx.tokenWrapper.wrap(amount0BN.toString());
|
||||
|
||||
const addLiquidityTx = assetHubApi.tx.assetConversion.addLiquidity(
|
||||
asset0,
|
||||
asset1,
|
||||
amount0BN.toString(),
|
||||
amount1BN.toString(),
|
||||
minAmount0BN.toString(),
|
||||
minAmount1BN.toString(),
|
||||
selectedAccount.address
|
||||
);
|
||||
|
||||
// Batch wrap + add liquidity
|
||||
tx = assetHubApi.tx.utility.batchAll([wrapTx, addLiquidityTx]);
|
||||
} else {
|
||||
// Direct add liquidity (no wrapping needed)
|
||||
tx = assetHubApi.tx.assetConversion.addLiquidity(
|
||||
asset0,
|
||||
asset1,
|
||||
amount0BN.toString(),
|
||||
amount1BN.toString(),
|
||||
minAmount0BN.toString(),
|
||||
minAmount1BN.toString(),
|
||||
selectedAccount.address
|
||||
);
|
||||
}
|
||||
const tx = assetHubApi.tx.assetConversion.addLiquidity(
|
||||
asset0Location,
|
||||
asset1Location,
|
||||
amount0BN.toString(),
|
||||
amount1BN.toString(),
|
||||
minAmount0BN.toString(),
|
||||
minAmount1BN.toString(),
|
||||
selectedAccount.address
|
||||
);
|
||||
|
||||
await tx.signAndSend(
|
||||
selectedAccount.address,
|
||||
|
||||
Reference in New Issue
Block a user