speeding up getAllTokens by using multicall instead of sequentially calling details for each token
This commit is contained in:
@@ -198,17 +198,75 @@ export function useGetPoolsByToken(tokenAddress: `0x${string}` | undefined, offs
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map to store available tokens with their swap routes
|
// First, fetch all tokens from all working pools
|
||||||
const tokenRoutesMap = new Map<string, AvailableToken>();
|
const poolTokensContracts = workingPools.map(poolAddress => ({
|
||||||
|
|
||||||
// For each working pool, fetch all tokens and track indices
|
|
||||||
for (const poolAddress of workingPools) {
|
|
||||||
try {
|
|
||||||
const tokensInPool = await publicClient.readContract({
|
|
||||||
address: poolAddress,
|
address: poolAddress,
|
||||||
abi: IPartyPoolABI,
|
abi: IPartyPoolABI,
|
||||||
functionName: 'allTokens',
|
functionName: 'allTokens',
|
||||||
}) as readonly `0x${string}`[];
|
}));
|
||||||
|
|
||||||
|
const poolTokensResults = await publicClient.multicall({
|
||||||
|
contracts: poolTokensContracts as any,
|
||||||
|
allowFailure: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build a flat list of all unique token addresses we need to query
|
||||||
|
const uniqueTokenAddresses = new Set<`0x${string}`>();
|
||||||
|
uniqueTokenAddresses.add(tokenAddress); // Add input token
|
||||||
|
|
||||||
|
poolTokensResults.forEach((result) => {
|
||||||
|
if (result.status === 'success') {
|
||||||
|
const tokens = result.result as readonly `0x${string}`[];
|
||||||
|
tokens.forEach(token => uniqueTokenAddresses.add(token));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const tokenAddressesArray = Array.from(uniqueTokenAddresses);
|
||||||
|
|
||||||
|
// Build multicall for all token symbols and decimals
|
||||||
|
const tokenDataContracts = tokenAddressesArray.flatMap(addr => [
|
||||||
|
{
|
||||||
|
address: addr,
|
||||||
|
abi: ERC20ABI,
|
||||||
|
functionName: 'symbol',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
address: addr,
|
||||||
|
abi: ERC20ABI,
|
||||||
|
functionName: 'decimals',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const tokenDataResults = await publicClient.multicall({
|
||||||
|
contracts: tokenDataContracts as any,
|
||||||
|
allowFailure: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Parse token data into a map
|
||||||
|
const tokenDataMap = new Map<string, { symbol: string | null; decimals: number | null }>();
|
||||||
|
for (let i = 0; i < tokenAddressesArray.length; i++) {
|
||||||
|
const symbolResult = tokenDataResults[i * 2];
|
||||||
|
const decimalsResult = tokenDataResults[i * 2 + 1];
|
||||||
|
tokenDataMap.set(tokenAddressesArray[i].toLowerCase(), {
|
||||||
|
symbol: symbolResult.status === 'success' ? (symbolResult.result as string) : null,
|
||||||
|
decimals: decimalsResult.status === 'success' ? Number(decimalsResult.result) : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map to store available tokens with their swap routes
|
||||||
|
const tokenRoutesMap = new Map<string, AvailableToken>();
|
||||||
|
|
||||||
|
// For each working pool, process tokens
|
||||||
|
for (let poolIdx = 0; poolIdx < workingPools.length; poolIdx++) {
|
||||||
|
const poolAddress = workingPools[poolIdx];
|
||||||
|
const poolTokensResult = poolTokensResults[poolIdx];
|
||||||
|
|
||||||
|
if (poolTokensResult.status !== 'success') {
|
||||||
|
console.error('Failed to fetch tokens for pool', poolAddress);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokensInPool = poolTokensResult.result as readonly `0x${string}`[];
|
||||||
|
|
||||||
// Find the input token index in this pool
|
// Find the input token index in this pool
|
||||||
const inputTokenIndex = tokensInPool.findIndex(
|
const inputTokenIndex = tokensInPool.findIndex(
|
||||||
@@ -220,6 +278,9 @@ export function useGetPoolsByToken(tokenAddress: `0x${string}` | undefined, offs
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inputTokenData = tokenDataMap.get(tokenAddress.toLowerCase());
|
||||||
|
const inputTokenDecimal = inputTokenData?.decimals ?? null;
|
||||||
|
|
||||||
// Process each token in the pool
|
// Process each token in the pool
|
||||||
for (let outputTokenIndex = 0; outputTokenIndex < tokensInPool.length; outputTokenIndex++) {
|
for (let outputTokenIndex = 0; outputTokenIndex < tokensInPool.length; outputTokenIndex++) {
|
||||||
const outputTokenAddress = tokensInPool[outputTokenIndex];
|
const outputTokenAddress = tokensInPool[outputTokenIndex];
|
||||||
@@ -229,24 +290,9 @@ export function useGetPoolsByToken(tokenAddress: `0x${string}` | undefined, offs
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the symbol of this token
|
const outputTokenData = tokenDataMap.get(outputTokenAddress.toLowerCase());
|
||||||
const outputTokenSymbol = await publicClient.readContract({
|
const outputTokenSymbol = outputTokenData?.symbol ?? null;
|
||||||
address: outputTokenAddress,
|
const outputTokenDecimal = outputTokenData?.decimals ?? null;
|
||||||
abi: ERC20ABI,
|
|
||||||
functionName: 'symbol',
|
|
||||||
}).catch(() => null);
|
|
||||||
|
|
||||||
const inputTokenDecimal = await publicClient.readContract({
|
|
||||||
address: tokenAddress,
|
|
||||||
abi: ERC20ABI,
|
|
||||||
functionName: 'decimals',
|
|
||||||
}).catch(() => null);
|
|
||||||
|
|
||||||
const outputTokenDecimal = await publicClient.readContract({
|
|
||||||
address: outputTokenAddress,
|
|
||||||
abi: ERC20ABI,
|
|
||||||
functionName: 'decimals',
|
|
||||||
}).catch(() => null);
|
|
||||||
|
|
||||||
// Skip tokens with the same symbol as the selected token
|
// Skip tokens with the same symbol as the selected token
|
||||||
if (!outputTokenSymbol || outputTokenSymbol === selectedTokenSymbol) {
|
if (!outputTokenSymbol || outputTokenSymbol === selectedTokenSymbol) {
|
||||||
@@ -278,9 +324,6 @@ export function useGetPoolsByToken(tokenAddress: `0x${string}` | undefined, offs
|
|||||||
outputTokenDecimal,
|
outputTokenDecimal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.error('Error fetching tokens from pool', poolAddress, err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const availableTokensList = Array.from(tokenRoutesMap.values());
|
const availableTokensList = Array.from(tokenRoutesMap.values());
|
||||||
@@ -326,55 +369,54 @@ export function useTokenDetails(userAddress: `0x${string}` | undefined) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const details: TokenDetails[] = [];
|
// Build multicall contracts array - 4 calls per token (name, symbol, decimals, balanceOf)
|
||||||
|
const contracts = tokens.flatMap((tokenAddress) => [
|
||||||
// Make individual calls for each token
|
{
|
||||||
for (let i = 0; i < tokens.length; i++) {
|
|
||||||
const tokenAddress = tokens[i];
|
|
||||||
try {
|
|
||||||
const [name, symbol, decimals, balance] = await Promise.all([
|
|
||||||
publicClient.readContract({
|
|
||||||
address: tokenAddress,
|
address: tokenAddress,
|
||||||
abi: ERC20ABI,
|
abi: ERC20ABI,
|
||||||
functionName: 'name',
|
functionName: 'name',
|
||||||
}).catch(() => 'Unknown'),
|
},
|
||||||
publicClient.readContract({
|
{
|
||||||
address: tokenAddress,
|
address: tokenAddress,
|
||||||
abi: ERC20ABI,
|
abi: ERC20ABI,
|
||||||
functionName: 'symbol',
|
functionName: 'symbol',
|
||||||
}).catch(() => '???'),
|
},
|
||||||
publicClient.readContract({
|
{
|
||||||
address: tokenAddress,
|
address: tokenAddress,
|
||||||
abi: ERC20ABI,
|
abi: ERC20ABI,
|
||||||
functionName: 'decimals',
|
functionName: 'decimals',
|
||||||
}).catch(() => 18),
|
},
|
||||||
publicClient.readContract({
|
{
|
||||||
address: tokenAddress,
|
address: tokenAddress,
|
||||||
abi: ERC20ABI,
|
abi: ERC20ABI,
|
||||||
functionName: 'balanceOf',
|
functionName: 'balanceOf',
|
||||||
args: [userAddress],
|
args: [userAddress],
|
||||||
}).catch(() => BigInt(0)),
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Execute multicall
|
||||||
|
const results = await publicClient.multicall({
|
||||||
|
contracts: contracts as any,
|
||||||
|
allowFailure: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Parse results
|
||||||
|
const details: TokenDetails[] = [];
|
||||||
|
for (let i = 0; i < tokens.length; i++) {
|
||||||
|
const baseIndex = i * 4;
|
||||||
|
const nameResult = results[baseIndex];
|
||||||
|
const symbolResult = results[baseIndex + 1];
|
||||||
|
const decimalsResult = results[baseIndex + 2];
|
||||||
|
const balanceResult = results[baseIndex + 3];
|
||||||
|
|
||||||
details.push({
|
details.push({
|
||||||
address: tokenAddress,
|
address: tokens[i],
|
||||||
name: name as string,
|
name: nameResult.status === 'success' ? (nameResult.result as string) : 'Unknown',
|
||||||
symbol: symbol as string,
|
symbol: symbolResult.status === 'success' ? (symbolResult.result as string) : '???',
|
||||||
decimals: Number(decimals),
|
decimals: decimalsResult.status === 'success' ? Number(decimalsResult.result) : 18,
|
||||||
balance: balance as bigint,
|
balance: balanceResult.status === 'success' ? (balanceResult.result as bigint) : BigInt(0),
|
||||||
index: i,
|
index: i,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
// Add token with fallback values if individual call fails
|
|
||||||
details.push({
|
|
||||||
address: tokenAddress,
|
|
||||||
name: 'Unknown',
|
|
||||||
symbol: '???',
|
|
||||||
decimals: 18,
|
|
||||||
balance: BigInt(0),
|
|
||||||
index: i,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTokenDetails(details);
|
setTokenDetails(details);
|
||||||
|
|||||||
Reference in New Issue
Block a user